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-2023 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-mp.h"
  29  #include "a68g-genie.h"
  30  #include "a68g-postulates.h"
  31  #include "a68g-parser.h"
  32  #include "a68g-options.h"
  33  #include "a68g-optimiser.h"
  34  #include "a68g-listing.h"
  35  
  36  #if defined (BUILD_WIN32)
  37  #include <windows.h>
  38  #endif
  39  
  40  //! @brief Safely get dir name from path.
  41  
  42  char *a68_dirname (char *src)
  43  {
  44    int len = (int) strlen (src) + 1;
  45    char *cpy = (char *) get_fixed_heap_space (len + 1);
  46    char *dst = (char *) get_fixed_heap_space (len + 1);
  47    ABEND (cpy == NO_TEXT, ERROR_OUT_OF_CORE, __func__);
  48    ABEND (dst == NO_TEXT, ERROR_OUT_OF_CORE, __func__);
  49    bufcpy (cpy, src, len);
  50    bufcpy (dst, dirname (cpy), len);
  51    return dst;
  52  }
  53  
  54  //! @brief Safely get basename from path.
  55  
  56  char *a68_basename (char *src)
  57  {
  58    int len = (int) strlen (src) + 1;
  59    char *cpy = (char *) get_fixed_heap_space (len + 1);
  60    char *dst = (char *) get_fixed_heap_space (len + 1);
  61    ABEND (cpy == NO_TEXT, ERROR_OUT_OF_CORE, __func__);
  62    ABEND (dst == NO_TEXT, ERROR_OUT_OF_CORE, __func__);
  63    bufcpy (cpy, src, len);
  64    bufcpy (dst, basename (cpy), len);
  65    return dst;
  66  }
  67  
  68  //! @brief Compute relative path.
  69  
  70  #if defined (BUILD_WIN32)
  71  
  72  static char *win32_slash (char *p)
  73  {
  74    char *q = p;
  75    while (*p != '\0') {
  76      if (*p == '\\') {
  77        *p = '/';
  78      }
  79      p++;
  80    }
  81    return q;
  82  }
  83  
  84  static char *win32_realpath (char *name, char *resolved)
  85  {
  86    char *res = NO_TEXT;
  87    if (name == NO_TEXT || name[0] == '\0') {
  88      return NO_TEXT;
  89    }
  90    if (resolved == NO_TEXT) {
  91      res = (char *) get_fixed_heap_space (PATH_MAX + 1);
  92      if (res == NO_TEXT) {
  93        return NO_TEXT;
  94      }
  95    } else {
  96      res = resolved;
  97    }
  98    int rc = GetFullPathName (name, PATH_MAX, res, (char **) NO_TEXT);
  99    if (rc == 0) {
 100      return NO_TEXT;
 101    } else {
 102      win32_slash (res);
 103      struct stat st;
 104      if (stat (res, &st) < 0) { // Should be 'lstat', but mingw does not have that.
 105        if (resolved == NO_TEXT) {
 106          free (res);
 107          return NO_TEXT;
 108        }
 109      }
 110    }
 111    return res;
 112  }
 113  
 114  #endif
 115  
 116  char *a68_relpath (char *p1, char *p2, char *fn)
 117  {
 118    char q[PATH_MAX + 1];
 119    bufcpy (q, p1, PATH_MAX);
 120    bufcat (q, "/", PATH_MAX);
 121    bufcat (q, p2, PATH_MAX);
 122    bufcat (q, "/", PATH_MAX);
 123    bufcat (q, fn, PATH_MAX);
 124  // Home directory shortcut ~ is a shell extension.
 125    if (strchr (q, '~') != NO_TEXT) {
 126      return NO_TEXT;
 127    }
 128    char *r = (char *) get_fixed_heap_space (PATH_MAX + 1);
 129    ABEND (r == NO_TEXT, ERROR_OUT_OF_CORE, __func__);
 130  // Error handling in the caller!
 131    errno = 0;
 132  #if defined (BUILD_WIN32)
 133    r = win32_realpath (q, NO_TEXT);
 134  #else
 135    r = realpath (q, NO_TEXT);
 136  #endif
 137    return r;
 138  }
 139  
 140  //! @brief PROC (STRING) STRING realpath
 141  
 142  void genie_realpath (NODE_T * p)
 143  {
 144    A68_REF str;
 145    char in[PATH_MAX + 1];
 146    char * out;
 147    POP_REF (p, &str);
 148    if (a_to_c_string (p, in, str) == NO_TEXT) {
 149      PUSH_REF (p, empty_string (p));
 150    } else {
 151  // Note that ~ is not resolved since that is the shell, not libc.
 152  #if defined (BUILD_WIN32)
 153      out = win32_realpath (in, NO_TEXT);
 154  #else
 155      out = realpath (in, NO_TEXT);
 156  #endif
 157      if (out == NO_TEXT) {
 158        PUSH_REF (p, empty_string (p));
 159      } else {
 160        PUSH_REF (p, c_to_a_string (p, out, PATH_MAX));
 161      }
 162    }
 163  }
 164