a68g-path.c

     
   1  //! @file a68g-path.c
   2  //! @author J. Marcel van der Veer
   3  
   4  //! @section Copyright
   5  //!
   6  //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
   7  //! Copyright 2001-2025 J. Marcel van der Veer [algol68g@xs4all.nl].
   8  
   9  //! @section License
  10  //!
  11  //! This program is free software; you can redistribute it and/or modify it 
  12  //! under the terms of the GNU General Public License as published by the 
  13  //! Free Software Foundation; either version 3 of the License, or 
  14  //! (at your option) any later version.
  15  //!
  16  //! This program is distributed in the hope that it will be useful, but 
  17  //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  18  //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  19  //! more details. You should have received a copy of the GNU General Public 
  20  //! License along with this program. If not, see [http://www.gnu.org/licenses/].
  21  
  22  //! @section Synopsis
  23  //!
  24  //! Low-level file path routines.
  25  
  26  #include "a68g.h"
  27  #include "a68g-prelude.h"
  28  #include "a68g-genie.h"
  29  
  30  #if defined (BUILD_WIN32)
  31  #include <windows.h>
  32  #endif
  33  
  34  //! @brief Safely get dir name from path.
  35  
  36  char *a68_dirname (char *src)
  37  {
  38    int len = (int) strlen (src) + 1;
  39    char *cpy = (char *) get_fixed_heap_space (len + 1);
  40    char *dst = (char *) get_fixed_heap_space (len + 1);
  41    ABEND (cpy == NO_TEXT, ERROR_OUT_OF_CORE, __func__);
  42    ABEND (dst == NO_TEXT, ERROR_OUT_OF_CORE, __func__);
  43    a68_bufcpy (cpy, src, len);
  44    a68_bufcpy (dst, dirname (cpy), len);
  45    return dst;
  46  }
  47  
  48  //! @brief Safely get basename from path.
  49  
  50  char *a68_basename (char *src)
  51  {
  52    int len = (int) strlen (src) + 1;
  53    char *cpy = (char *) get_fixed_heap_space (len + 1);
  54    char *dst = (char *) get_fixed_heap_space (len + 1);
  55    ABEND (cpy == NO_TEXT, ERROR_OUT_OF_CORE, __func__);
  56    ABEND (dst == NO_TEXT, ERROR_OUT_OF_CORE, __func__);
  57    a68_bufcpy (cpy, src, len);
  58    a68_bufcpy (dst, basename (cpy), len);
  59    return dst;
  60  }
  61  
  62  //! @brief Compute relative path.
  63  
  64  #if defined (BUILD_WIN32)
  65  
  66  static char *win32_slash (char *p)
  67  {
  68    char *q = p;
  69    while (*p != '\0') {
  70      if (*p == '\\') {
  71        *p = '/';
  72      }
  73      p++;
  74    }
  75    return q;
  76  }
  77  
  78  static char *win32_realpath (char *name, char *resolved)
  79  {
  80    char *res = NO_TEXT;
  81    if (name == NO_TEXT || name[0] == '\0') {
  82      return NO_TEXT;
  83    }
  84    if (resolved == NO_TEXT) {
  85      res = (char *) get_fixed_heap_space (PATH_MAX + 1);
  86      if (res == NO_TEXT) {
  87        return NO_TEXT;
  88      }
  89    } else {
  90      res = resolved;
  91    }
  92    int ret = GetFullPathName (name, PATH_MAX, res, (char **) NO_TEXT);
  93    if (ret == 0) {
  94      return NO_TEXT;
  95    } else {
  96      win32_slash (res);
  97      struct stat st;
  98      if (stat (res, &st) < 0) { // Should be 'lstat', but mingw does not have that.
  99        if (resolved == NO_TEXT) {
 100          free (res);
 101          return NO_TEXT;
 102        }
 103      }
 104    }
 105    return res;
 106  }
 107  
 108  #endif
 109  
 110  char *a68_relpath (char *p1, char *p2, char *fn)
 111  {
 112    char q[PATH_MAX + 1];
 113    ASSERT (p1 != NO_TEXT);
 114    ASSERT (p2 != NO_TEXT);
 115    ASSERT (fn != NO_TEXT);
 116  #if defined (BUILD_WIN32)
 117    if (p2[0] == '\\') {
 118      a68_bufcpy (q, p2, PATH_MAX);
 119    } else {
 120      a68_bufcpy (q, p1, PATH_MAX);
 121      a68_bufcat (q, "\\", PATH_MAX);
 122      a68_bufcat (q, p2, PATH_MAX);
 123    }
 124    a68_bufcat (q, "\\", PATH_MAX);
 125    a68_bufcat (q, fn, PATH_MAX);
 126  #else
 127    if (p2[0] == '/') {
 128  // Absolute path starting at root.
 129      a68_bufcpy (q, p2, PATH_MAX);
 130    } else if (p2[0] == '~') {
 131  // "~" is a shell extension, not Unix.
 132  // We handle it anyway, as a courtesy.
 133      a68_bufcpy (q, getenv("HOME"), PATH_MAX);
 134      a68_bufcat (q, &p2[1], PATH_MAX);
 135    } else {
 136      a68_bufcpy (q, p1, PATH_MAX);
 137      a68_bufcat (q, "/", PATH_MAX);
 138      a68_bufcat (q, p2, PATH_MAX);
 139    }
 140    a68_bufcat (q, "/", PATH_MAX);
 141    a68_bufcat (q, fn, PATH_MAX);
 142  #endif
 143  // Error handling in the caller!
 144    errno = 0;
 145  #if defined (BUILD_WIN32)
 146    char *r = win32_realpath (q, NO_TEXT);
 147  #else
 148    char *r = realpath (q, NO_TEXT);
 149  #endif
 150    if (r != NO_TEXT) {
 151      size_t len = strlen (r) + 1;
 152      char *rc = (char *) get_fixed_heap_space (len);
 153      ABEND (rc == NO_TEXT, ERROR_OUT_OF_CORE, __func__);
 154      a68_bufcpy (rc, r, len);
 155      free (r);
 156      return rc;
 157    } else {
 158      return NO_TEXT;
 159    }
 160  }
 161  
 162  //! @brief PROC (STRING) STRING realpath
 163  
 164  void genie_realpath (NODE_T * p)
 165  {
 166    A68_REF str;
 167    POP_REF (p, &str);
 168    char in[PATH_MAX + 1];
 169    if (a_to_c_string (p, in, str) == NO_TEXT) {
 170      PUSH_REF (p, empty_string (p));
 171    } else {
 172  // Note that ~ is not resolved since that is the shell, not libc.
 173  #if defined (BUILD_WIN32)
 174      char *out = win32_realpath (in, NO_TEXT);
 175  #else
 176      char *out = realpath (in, NO_TEXT);
 177  #endif
 178      if (out == NO_TEXT) {
 179        PUSH_REF (p, empty_string (p));
 180      } else {
 181        PUSH_REF (p, c_to_a_string (p, out, PATH_MAX));
 182      }
 183    }
 184  }
 185  
     


© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)