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