pathname_xsb.c

00001 /* File:      pathname_xsb.c -- utilities to manipulate path/file names
00002 ** Author(s): kifer, kostis
00003 ** Contact:   xsb-contact@cs.sunysb.edu
00004 ** 
00005 ** Copyright (C) The Research Foundation of SUNY, 1998
00006 ** 
00007 ** XSB is free software; you can redistribute it and/or modify it under the
00008 ** terms of the GNU Library General Public License as published by the Free
00009 ** Software Foundation; either version 2 of the License, or (at your option)
00010 ** any later version.
00011 ** 
00012 ** XSB is distributed in the hope that it will be useful, but WITHOUT ANY
00013 ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00014 ** FOR A PARTICULAR PURPOSE.  See the GNU Library General Public License for
00015 ** more details.
00016 ** 
00017 ** You should have received a copy of the GNU Library General Public License
00018 ** along with XSB; if not, write to the Free Software Foundation,
00019 ** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 **
00021 ** $Id: pathname_xsb.c,v 1.29 2006/04/17 20:54:57 tswift Exp $
00022 ** 
00023 */
00024 
00025 #include "xsb_config.h"
00026 #include "xsb_debug.h"
00027 
00028 #ifdef WIN_NT
00029 #include <direct.h>
00030 #include <io.h>
00031 #else
00032 #include <unistd.h>
00033 #endif
00034 
00035 #include <stdio.h>
00036 #include <errno.h>
00037 #include <string.h>
00038 #include <stdlib.h>
00039 #include <ctype.h>
00040 #include <sys/types.h>
00041 #include <sys/stat.h>
00042 /* wind2unix.h must be included after sys/stat.h */
00043 #include "wind2unix.h"
00044 #include "export.h"
00045 
00046 #if (!defined(WIN_NT))
00047 #include <pwd.h>
00048 #endif
00049 
00050 #include "setjmp_xsb.h"
00051 #include "auxlry.h"
00052 #include "context.h"
00053 #include "psc_xsb.h"
00054 #include "cell_xsb.h"
00055 #include "cinterf.h"
00056 #include "error_xsb.h"
00057 #include "flags_xsb.h"
00058 #include "extensions_xsb.h"
00059 #include "memory_xsb.h"
00060 
00061 /*=========================================================================*/
00062 
00063 #define DDOT    ".."
00064 #define DOT     "."
00065 
00066 extern char *user_home_gl;        /* from main_xsb.c: the user $HOME dir or
00067                                      install dir, if $HOME is null */
00068 static char *rectify_pathname(char *, char *);
00069 extern void transform_cygwin_pathname(char *);
00070 
00071 /*=========================================================================*/
00072 
00073 /* check if the path name is absolute */
00074 xsbBool is_absolute_filename(char *filename) {
00075 
00076 #if defined(WIN_NT) 
00077   /*  If the file name begins with a "\" or with an "X:", where X is some
00078    *  character, then the file name is absolute.
00079    *  We also assume if it starts with a // or /cygdrive/, it's absolute */
00080   if ( (filename[0] == SLASH) || 
00081        (isalpha(filename[0]) && filename[1] == ':') ||
00082        (filename[0] == '/')
00083        )
00084     return TRUE;
00085 #else /* Unix */
00086   if (filename[0] == '/')
00087     return TRUE;
00088 #endif
00089 
00090   return FALSE;
00091 }
00092   
00093 
00094 /* if file is a directory and is missing a trailing slash, add it 
00095    passes file name and the slash to be added. can be backslash on NT
00096    Also, strips off excessive trailing slashes. 
00097 */
00098 char *dirname_canonic(char *filename) {
00099   char canonicized[MAXPATHLEN];
00100   int retcode, len = strlen(filename);
00101   struct stat fileinfo;
00102   rectify_pathname(filename, canonicized);
00103   retcode = stat(canonicized, &fileinfo);
00104 
00105   /* if directory, add trailing slash */
00106   if ((retcode==0) && S_ISDIR(fileinfo.st_mode)
00107       && (canonicized[len-1] != SLASH)) {
00108     canonicized[len] = SLASH;
00109     canonicized[len+1] = '\0';
00110   }
00111   return string_find(canonicized,1);
00112 }
00113 
00114 
00115 static void normalize_file_slashes(char *path) {
00116   int ANTISLASH = (SLASH == '/' ? '\\' : '/');
00117 
00118   path = strchr(path,ANTISLASH);
00119   while (path != NULL) {
00120     *path = SLASH;
00121     path = strchr(path,ANTISLASH);
00122   }
00123 }
00124 
00125 /* Like tilde_expand_filename, but doesn't rectify */
00126 char *tilde_expand_filename_norectify(char *filename, char *expanded) {
00127   
00128 #if defined(WIN_NT)
00129   strcpy(expanded, filename);
00130   transform_cygwin_pathname(expanded);
00131   normalize_file_slashes(expanded);
00132   return expanded;
00133 
00134 #else /* Unix */
00135   char *path_prefix;        /* ptr to a (sub)string containing what will
00136                                become the prefix for the absolute filename. */
00137   char *path_suffix;        /* ptr to a (sub)string containing what will
00138                                become the suffix for the absolute filename. */
00139   char username[MAXNAME]; /* the username if filename has ~<name> */
00140   int username_len;
00141   struct passwd *pw_struct;     /* receives passwd structure from getpwnum() */
00142 
00143   if (filename[0] != '~') {
00144     strcpy(expanded, filename);
00145     normalize_file_slashes(expanded);
00146     return expanded;
00147   } 
00148   if (filename[1] == '/' || filename[1] == '\0') {
00149     /*  The file name begins with "~/" or is simply "~" -- so replace
00150         '~' with the user's home directory. */
00151     path_prefix = user_home_gl;
00152     path_suffix = filename + 1;
00153   } else {
00154     /*  The file name begins with ~<username>.  Use a system call to
00155         determine this directory's path. */
00156     path_prefix = path_suffix = filename + 1;
00157     while ( (*path_suffix != '\0') && (*path_suffix != '/') )
00158       path_suffix++;
00159     username_len = path_suffix - path_prefix;
00160     memmove(username, path_prefix, username_len);
00161     username[username_len] = '\0';
00162     
00163     pw_struct = (struct passwd *) getpwnam(username);
00164     if (!pw_struct) {
00165       /*  The system has no info on this user, so we can't
00166           construct the absolute path -- abort. */
00167       char message[100];
00168       sprintf(message, "[PATHNAME] `%s': unknown user\n", username);
00169       xsb_abort(message);
00170     } else
00171       path_prefix = pw_struct -> pw_dir;
00172   }
00173     
00174   sprintf(expanded, "%s%c%s", path_prefix, SLASH, path_suffix);
00175   normalize_file_slashes(expanded);
00176   return expanded;
00177 #endif /* Unix */
00178 }
00179 
00180 /*
00181 ** Like expand_filename, but ONLY expands Unix tilde by replacing '~', '~user'
00182 ** with the home directory of the appropriate user.
00183 ** Does nothing on Windows.
00184 */
00185 char *tilde_expand_filename(char *filename) {
00186   char aux_filename[MAXPATHLEN];
00187   char absolute_filename[MAXPATHLEN]; /* abs filename composed here */
00188 
00189   tilde_expand_filename_norectify(filename, aux_filename);
00190   return string_find(rectify_pathname(aux_filename, absolute_filename),1);
00191 }
00192 
00193 
00194 /*
00195  *  Return full path name for the file passed in as argument.
00196  *  This is called from cinterf and may be called before XSB is initialized,
00197  *  so to make thread-safe, malloc space to return value (leaky)
00198  */
00199 
00200 char *expand_filename(char *filename) {
00201   char aux_filename[MAXPATHLEN],
00202    aux_filename2[MAXPATHLEN];
00203   char *absolute_filename = mem_alloc(MAXPATHLEN,OTHER_SPACE); // since xsb may not be initialized
00204 
00205   if (is_absolute_filename(filename)) {
00206     return rectify_pathname(filename, absolute_filename);
00207 #ifndef WIN_NT
00208   } else if (filename[0] == '~') {
00209     tilde_expand_filename_norectify(filename, aux_filename);
00210     return rectify_pathname(aux_filename, absolute_filename);
00211 #endif
00212   } else {
00213     getcwd(aux_filename2, MAXPATHLEN-1);
00214     sprintf(aux_filename, "%s%c%s", aux_filename2,
00215             SLASH, filename);
00216     return rectify_pathname(aux_filename, absolute_filename);
00217   }
00218 }
00219 
00220 /* strip names from the back of path 
00221    PATH is the path name from which to strip.
00222    HOW_MANY is the number of names to strip.
00223    E.g., strip_names_from_path("a/b/c/d", 2) returns "a/b" 
00224 
00225    This function is smart about . and ..: 
00226            strip_names_from_path("a/b/c/./d", 2)
00227    is still "a/b" and
00228            strip_names_from_path("a/b/c/../d", 2)
00229    is "a".
00230    If we ask to strip too many names from path, it'll abort.
00231 
00232    This function copies the result into a large buffer, so we can add more
00233    stuff to it. These buffers stay forever, but we call this func only a couple
00234    of times, so it's ok. */
00235 DllExport char * call_conv strip_names_from_path(char* path, int how_many)
00236 {
00237   int i, abort_flag=FALSE;
00238   char *cutoff_ptr;
00239   char *buffer = malloc(MAXPATHLEN);  // can't change to mem_alloc for mt, lock not initted?
00240 
00241 #ifdef SIMPLESCALAR
00242   if (!buffer)
00243     printf("no space to allocate buffer in strip_names_from_path.\n");
00244   
00245 /*   rectify_pathname(path,buffer); */
00246   strcpy(buffer,path);
00247 
00248   cutoff_ptr = buffer + strlen(buffer);
00249 
00250   while (cutoff_ptr != buffer && how_many > 0) {
00251     if (*cutoff_ptr == SLASH) {
00252       how_many--;
00253       *cutoff_ptr = '\0';
00254     }
00255     cutoff_ptr--;
00256   }
00257 
00258   if (how_many > 0)
00259       xsb_abort("[PATHNAME] There is no directory %d levels below %s",
00260                 how_many, path);
00261   return buffer;
00262 
00263 #endif
00264 
00265   rectify_pathname(path,buffer);
00266 
00267   for (i=0; i < how_many; i++) {
00268     if (abort_flag) {
00269       xsb_abort("[PATHNAME] There is no directory %d levels below %s",
00270                 how_many, path);
00271     }
00272     cutoff_ptr = strrchr(buffer, SLASH);
00273     if (cutoff_ptr == NULL)
00274       return "";
00275     if ((cutoff_ptr - buffer) > 0)
00276       /* we have more than just a slash between the beginning of buffer and
00277          cutoff_ptr: replace slash with end of string */
00278       *cutoff_ptr = '\0';
00279     else {       /* we are at the top of the file hierarchy */
00280       *(cutoff_ptr+1) = '\0';
00281       abort_flag=TRUE;
00282     }
00283   }
00284   return buffer;
00285 }
00286 
00287 
00288 /* Get the base name of PATH, e.g., basename of a/b/c is c if path is a/b/c/
00289    then basename is "". This op preserves extension, i.e., basename of a/b.c 
00290    is b.c */
00291 static char *get_file_basename(char *path) {
00292   char *ptr;
00293   ptr = strrchr(path, SLASH);
00294   if (ptr == NULL)
00295     return path;
00296   else
00297     return ptr+1;
00298 }
00299 
00300 /* get directory part of PATH. It first rectifies PATH, then returns result.
00301    E.g., a/b/.././c --> a/ (even if c is a directory itself)
00302    a/b/ --> a/b/ 
00303    Doesn't expand the directory name.
00304    Always leaves trailing slash at the end. 
00305 
00306    Expects a string storage as 2nd arg, returns second arg.
00307 */
00308 static char *get_file_dirname(char *path, char *dir) {
00309   char *ptr;
00310   ptr = strrchr(rectify_pathname(path,dir), SLASH);
00311   if (ptr == NULL)
00312     /* No slash in filename, return empty string */
00313     return "";
00314   /* the whole thing might be just "/". In this case, it is the dirname of the
00315      file */
00316   else if (*ptr==SLASH && *(ptr+1)=='\0')
00317     return dir;
00318   else {
00319     *(ptr+1) = '\0';
00320     return dir;
00321   }
00322 }
00323 
00324 /* Get file extension, i.e., "c" in abc.c, etc. If the file name is of the form
00325    ".abc", where none of a,b,c is a '.', then the extension is empty string. */
00326 
00327 static char *get_file_extension(char *path) {
00328   char *ptr, *base=get_file_basename(path);
00329   ptr = strrchr(base, '.');
00330   if ((ptr==base) || (ptr==NULL))
00331     return "";
00332   else return (ptr+1);
00333 }
00334 
00335 #define MAXPATHNAMES 256 /* max number of file names in a path name */
00336 
00337 /* 
00338 ** Go over path name and get rid of `..', `.', and multiple slashes 
00339 ** Won't delete leading `..'.
00340 ** Expects two strings (with allocated storage) as params: the input path and
00341 ** the output path. Returns the second argument.
00342 */
00343 static char *rectify_pathname(char *inpath, char *outpath) {
00344   char names[MAXPATHNAMES][MAXNAME];  /* array of filenames in inpath.
00345                                          1st index: enumerates names in inpath;
00346                                          2nd index: scan file names */
00347   char expanded_inpath[MAXPATHLEN];
00348   char *inptr1, *inptr2, *inpath_end;
00349   int length; /* length=inptr2-inptr1 */
00350   int i, outidx=0, nameidx=0; /* nameidx: 1st index to names */
00351   xsbBool leading_slash, leading_slash2, trailing_slash;
00352 
00353   tilde_expand_filename_norectify(inpath, expanded_inpath);
00354   
00355   /* initialization */
00356   inptr1 = inptr2 = expanded_inpath;
00357   inpath_end = expanded_inpath + strlen(expanded_inpath);
00358 
00359   /* check if expanded inpath has trailing/leading slash */
00360   leading_slash = (*expanded_inpath == SLASH ? TRUE : FALSE);
00361 #if defined(WIN_NT) || defined(CYGWIN)
00362   /* In windows, the leading \\foo means remote drive;
00363      In CYGWIN, absolute path starts with //driveletter */
00364   leading_slash2 = (*(expanded_inpath+1) == SLASH ? TRUE : FALSE);
00365 #else
00366   leading_slash2 = FALSE;
00367 #endif
00368   trailing_slash = (*(inpath_end - 1) == SLASH ? TRUE : FALSE);
00369 
00370   while ( inptr2 < inpath_end ) {
00371     inptr2 = strchr(inptr1, SLASH);
00372     if (inptr2==NULL)
00373       inptr2 = inpath_end;
00374 
00375     /* skip slashes */
00376     if ((length = inptr2 - inptr1) == 0) {
00377       if (inptr2 == inpath_end)
00378         break; /* out of the while loop */
00379       else {
00380         inptr2++;
00381         inptr1++;
00382         continue;
00383       }
00384     }
00385 
00386     switch (length) {
00387     case 1:
00388       if (*inptr1 == '.' && inptr1 != expanded_inpath) {
00389         inptr1 = inptr2;
00390         continue; /* the loop */
00391       }
00392       break; /* we found a file name, will process it */
00393     case 2: 
00394       if ((*inptr1 == '.') && (*(inptr1+1) == '.')) {
00395         nameidx--; /* drop the previous file name from the names array */
00396         if (nameidx < 0) {
00397           /* These are leading ..'s -- leave them */
00398           nameidx++;
00399           break;
00400         } else if (strcmp(names[nameidx], "..") == 0) {
00401           /* the previous name was also ".." -- leave the ..'s intact: we must
00402              be looking at the leading sequence of ../../../something */
00403           nameidx++;
00404         } else {
00405           /* Discard .. and the previous file name */
00406           inptr1 = inptr2;
00407           continue;
00408         }
00409       }
00410       break;
00411     } /* done processing '.' and '..' */
00412     
00413     /* copy the filename just found to the right slot in the names array */
00414     strncpy(names[nameidx], inptr1, length);
00415     names[nameidx][length] = '\0'; /* make a string out of the file name */
00416     nameidx++;
00417     inptr1=inptr2;
00418     if (nameidx >= MAXPATHNAMES)
00419       xsb_abort("[PATHNAME] Directory depth in pathname exceeds maximum, %s",
00420                 inpath);
00421   }
00422 
00423   /* at this point, we've copied all file names into names array and eliminated
00424      . and .. (in case of .. we also got rid of the preceding file name).
00425      So, we are ready to construct  the outpath. */
00426 
00427   if (leading_slash) {
00428     outpath[outidx] = SLASH;
00429     outidx++;
00430   }
00431   if (leading_slash && leading_slash2) {
00432 #if defined(CYGWIN)
00433     strncpy(outpath+outidx,"cygdrive",8);
00434     outidx += 8;
00435 #endif
00436     outpath[outidx] = SLASH;
00437     outidx++;
00438   }
00439 
00440   for (i=0; i<nameidx; i++) {
00441     strcpy(outpath+outidx, names[i]);
00442     outidx = outidx + strlen(names[i]);
00443     /* put shash in place of '\0', if we are not at the end yet */
00444     if (i < nameidx-1) {
00445       outpath[outidx] = SLASH;
00446       outidx++;
00447     }
00448   }
00449 
00450   /* don't add trailing slash if the file name is "/" */
00451   if (trailing_slash && (nameidx > 0)) {
00452     outpath[outidx] = SLASH;
00453     outpath[outidx+1] = '\0';
00454   }
00455   return(outpath);
00456 }
00457 
00458 /* Takes filename, gets back dir, base (sans the extension), and extension. 
00459    The dirname isn't expanded, even for tildas, but it is rectified.
00460    +++NOTE: this procedure modifies the input file name by inserting '\0' in
00461    place of the dot that separates the extension from the base name.
00462    This is ok, since this function is used only as a built in, so the input
00463    file name is discarded anyway. 
00464  */
00465 void parse_filename(char *filename, char **dir, char **base, char **extension)
00466 {
00467   char absolute_dirname[MAXPATHLEN]; /* abs dirname composed here */
00468   char basename[MAXNAME];           /* the rest of the filename  */
00469 
00470   *base = strcpy(basename, get_file_basename(filename));
00471   *dir = get_file_dirname(filename, absolute_dirname);
00472   *extension = get_file_extension(basename);
00473   /* cut off the extension from the base */
00474   if (*extension != "")
00475     *(*extension-1) = '\0'; 
00476   *base = string_find(*base,1);
00477   *dir = string_find(*dir,1);
00478   *extension = string_find(*extension,1);
00479   return;
00480 }
00481 
00482 /* transform_cygwin_pathname takes cygwin-like pathnames
00483 /cygdrive/D/..  and transforms them into windows-like pathnames D:\
00484 (in-place).  It assumes that the given pathname is a valid cygwin
00485 absolute pathname */
00486 
00487 void transform_cygwin_pathname(char *filename) 
00488 {
00489   char *pointer;
00490   char tmp[MAXPATHLEN];
00491   int diff;
00492 
00493   if (filename[0] == '/') {
00494     if (filename[1] == '/') diff = 1;
00495     else if (filename[1] == 'c' &&
00496              filename[2] == 'y' &&
00497              filename[3] == 'g' &&
00498              filename[4] == 'd' &&
00499              filename[5] == 'r' &&
00500              filename[6] == 'i' &&
00501              filename[7] == 'v' &&
00502              filename[8] == 'e' &&
00503              filename[9] == '/') diff = 9;
00504     else {
00505       strcpy(tmp,filename);
00506       strcpy(filename,(char *)flags[USER_HOME]);
00507       strcpy(filename+strlen((char *)flags[USER_HOME]),tmp);
00508       return;
00509     }
00510 
00511     pointer=filename+diff+1;
00512     filename[0]=*pointer;
00513     filename[1]=':';
00514     filename[2]='\\';
00515     for(pointer+=1;*pointer;pointer++) 
00516       if (*pointer == '/')
00517         *(pointer-diff) = '\\';
00518       else
00519         *(pointer-diff) = *pointer;
00520   
00521     *(pointer-diff) = '\0';
00522     return;
00523   }
00524 }
00525 
00526 /*=========================================================================*/
00527 #ifdef WIN_NT
00528 #define not_a_dir(fileinfo) !(fileinfo.st_mode & _S_IFDIR)
00529 #else
00530 #define not_a_dir(fileinfo) !(fileinfo.st_mode & S_IFDIR)
00531 #endif
00532 
00533 char *existing_file_extension(char *basename)
00534 {
00535   char filename[MAXPATHLEN];
00536   struct stat fileinfo;
00537 
00538   strcpy(filename, basename); strcat(filename, XSB_SRC_EXTENSION_STRING);
00539   /*  +1 skips the "."   */
00540   if (! stat(filename, &fileinfo) && not_a_dir(fileinfo)) return XSB_SRC_EXTENSION_STRING+1;
00541 
00542   strcpy(filename, basename); strcat(filename, ".c");
00543   if (! stat(filename, &fileinfo) && not_a_dir(fileinfo)) return "c";
00544 
00545   strcpy(filename, basename); strcat(filename, ".cpp");
00546   if (! stat(filename, &fileinfo) && not_a_dir(fileinfo)) return "cpp";
00547 
00548   strcpy(filename, basename); strcat(filename, ".pl");
00549   if (! stat(filename, &fileinfo) && not_a_dir(fileinfo)) return "pl";
00550 
00551   strcpy(filename, basename);
00552   if (! stat(filename, &fileinfo) && not_a_dir(fileinfo)) return ""; /* no extension */
00553 
00554   sprintf(filename, "%s%s", basename, XSB_OBJ_EXTENSION_STRING);
00555   /*  +1 skips the "."   */
00556   if (! stat(filename, &fileinfo) && not_a_dir(fileinfo)) return XSB_OBJ_EXTENSION_STRING+1;
00557 
00558   return NULL; /* signifies that the search was unsuccessful */
00559 }
00560 
00561 /*=========================================================================*/
00562 
00563 xsbBool almost_search_module(CTXTdeclc char *filename)
00564 {
00565   char *fullname, *dir, *basename, *extension;
00566   struct stat fileinfo;
00567 
00568   fullname = tilde_expand_filename(filename);
00569   parse_filename(fullname, &dir, &basename, &extension);
00570   if (! strcmp(filename, basename)) { /* only a module name given */
00571     /* this is the case that we have to resort to a non-deterministic
00572      * search in Prolog using the predicate libpath/1; that's why the
00573      * function is called "almost_".  Note that arguments 4 and 5 are
00574      * left unbound here.
00575      */
00576     ctop_string(CTXTc 2, dir);
00577     ctop_string(CTXTc 3, filename); /* Mod = FileName */
00578   } else { /* input argument is a full file name */
00579     if (! strcmp(extension, "")) {
00580       extension = existing_file_extension(fullname);
00581       if (! extension) return FALSE; /* file was not found */
00582       extension = string_find(extension,1);
00583     } else {
00584       if (stat(fullname, &fileinfo) && !strcmp(dir,"")) {
00585         /* file not found, so let search through dirs try to find it */ 
00586         ctop_string(CTXTc 2, dir);
00587         ctop_string(CTXTc 3, basename);
00588         ctop_string(CTXTc 4, extension);
00589         return TRUE;
00590       }
00591     }
00592     if (! strcmp(dir, "")) {
00593       char dot_dir[MAXPATHLEN];
00594       dot_dir[0] = '.';
00595       dot_dir[1] = SLASH;
00596       dot_dir[2] = '\0';
00597       ctop_string(CTXTc 2, string_find(dot_dir, 1));
00598       strcat(dot_dir, basename); /* dot_dir is updated here */
00599       ctop_string(CTXTc 5, string_find(dot_dir, 1));
00600     } else {
00601       char dirtmp[MAXPATHLEN];
00602       ctop_string(CTXTc 2, dir);
00603       strcpy(dirtmp,dir);
00604       strcat(dirtmp, basename);
00605       ctop_string(CTXTc 5, string_find(dirtmp, 1));
00606     }
00607     ctop_string(CTXTc 3, basename);
00608     ctop_string(CTXTc 4, extension);
00609   }
00610   return TRUE;
00611 }
00612 
00613 /*=========================================================================*/

Generated on Wed Jul 26 13:30:42 2006 for XSB by  doxygen 1.4.5