sdk-core v2026.11.05
PS2 SDK - core tier (build-critical EE/IOP modules)
Loading...
Searching...
No Matches
cwd.c
1/*
2# _____ ___ ____ ___ ____
3# ____| | ____| | | |____|
4# | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5#-----------------------------------------------------------------------
6# Copyright 2001-2004, ps2dev - http://www.ps2dev.org
7# Licenced under Academic Free License version 2.0
8# Review ps2sdk README & LICENSE files for further details.
9*/
10
11#include <stdio.h>
12#include <unistd.h>
13#include <string.h>
14#include <ctype.h>
15#include <sys/param.h>
16#include <dirent.h>
17#include <errno.h>
18#include <stdlib.h>
19
20/* the present working directory variable. */
21char __cwd[MAXNAMLEN + 1] = { 0 };
22
23/* the length of the present working directory variable. */
24size_t __cwd_len = 0;
25
26#define defaultCWD "host:"
27
28enum SeparatorType {
29 SeparatorTypeNotFound,
30 SeparatorTypeNone,
31 SeparatorTypePOSIX,
32 SeparatorTypeWindows,
33};
34
35#define isnum(c) ((c) >= '0' && (c) <= '9')
36
37/* Return the number of bytes taken up by the "drive:" prefix,
38 or -1 if it's not found */
39int __get_drive(const char *dev, enum SeparatorType *usePOSIXSeparator, int maxLen)
40{
41 const char *t;
42 const char *d;
43 int devname_len;
44
45 /* Skip leading spaces */
46 for (d = dev; ((int)(d - dev) < maxLen) && *d == ' '; d += 1);
47
48 /* Get colon position */
49 for (t = d; ((int)(t - dev) < maxLen) && *t && *t != ':'; t += 1);
50 if (*t != ':')
51 {
52 *usePOSIXSeparator = SeparatorTypeNotFound;
53 return 0;
54 }
55
56 /* Reduce length to not include index */
57 for (devname_len = (int)(t - d); isnum(d[devname_len - 1]); devname_len -= 1);
58
59 *usePOSIXSeparator = SeparatorTypePOSIX;
60 switch (devname_len)
61 {
62 case 3:
63 /* These drivers don't have separator */
64 if ((memcmp(d, "rom", devname_len) == 0) || (memcmp(d, "hdd", devname_len) == 0))
65 *usePOSIXSeparator = SeparatorTypeNone;
66 break;
67 case 5:
68 /* These drivers use \ as separator */
69 if ((memcmp(d, "cdrom", devname_len) == 0))
70 *usePOSIXSeparator = SeparatorTypeWindows;
71 break;
72 case 6:
73 /* These drivers don't have separator */
74 if ((memcmp(d, "usbkbd", devname_len) == 0))
75 *usePOSIXSeparator = SeparatorTypeNone;
76 break;
77 default:
78 break;
79 }
80
81 /* Return the length of the whole device name portion, including:
82 * - leading spaces
83 * - device name
84 * - device index
85 * - colon
86 */
87 return (t - dev) + 1;
88}
89
90char *getcwd(char *buf, size_t size)
91{
92 if (!buf)
93 {
94 size = __cwd_len + 1;
95 buf = malloc(size);
96 if (!buf)
97 {
98 errno = ENOMEM;
99 return NULL;
100 }
101 }
102 else if (!size)
103 {
104 errno = EINVAL;
105 return NULL;
106 }
107 else if (size < (__cwd_len + 1))
108 {
109 errno = ERANGE;
110 return NULL;
111 }
112
113 snprintf(buf, size, "%.*s", __cwd_len, __cwd);
114 return buf;
115}
116
117/* Normalize a pathname (without leading "drive:") by removing
118 . and .. components, duplicated /, etc. */
119static int __path_normalize(char *out, int len, int posixSeparator)
120{
121 int i, j;
122 int first, next;
123 char separator = posixSeparator ? '/' : '\\';
124 size_t out_len = strnlen(out, len);
125
126 out_len += 2;
127 if (out_len >= len) return -10;
128
129 /* First append "/" to make the rest easier */
130 out[out_len - 1] = separator;
131 out[out_len] = 0;
132
133 // Convert separators to the specified one
134 for (size_t i = 0; i < out_len; i++) {
135 if (out[i] == '/' || out[i] == '\\') {
136 out[i] = separator;
137 }
138 }
139
140 /* Convert "//" to "/" */
141 for(i = 0; (i < out_len) && out[i+1]; i++) {
142 if(out[i] == separator && out[i+1] == separator) {
143 for(j=i+1; out[j]; j++)
144 out[j] = out[j+1];
145 i--;
146 }
147 }
148
149 /* Convert "/./" to "/" */
150 for(i = 0; (i < out_len) && out[i] && out[i+1] && out[i+2]; i++) {
151 if(out[i] == separator && out[i+1] == '.' && out[i+2] == separator) {
152 for(j = i+1; out[j]; j++)
153 out[j] = out[j+2];
154 i--;
155 }
156 }
157
158 /* Convert "/asdf/../" to "/" until we can't anymore. Also
159 * convert leading "/../" to "/" */
160 first = next = 0;
161 while((first < out_len) && (next < out_len)) {
162 /* If a "../" follows, remove it and the parent */
163 if(out[next+1] && out[next+1] == '.' &&
164 out[next+2] && out[next+2] == '.' &&
165 out[next+3] && out[next+3] == separator) {
166 for(j=0; out[first+j+1]; j++)
167 out[first+j+1] = out[next+j+4];
168 first = next = 0;
169 continue;
170 }
171
172 /* Find next slash */
173 first = next;
174 for(next= first+1; out[next] && out[next] != separator; next++)
175 continue;
176 if(!out[next]) break;
177 }
178
179 /* Remove trailing "/" just if it's not the root */
180 for(i = 1; (i < out_len) && out[i]; i++)
181 continue;
182 if(i > 1 && out[i-1] == separator)
183 out[i-1] = 0;
184
185 return 0;
186}
187
188/* Convert relative path to absolute path. */
189int __path_absolute(const char *in, char *out, int len)
190{
191 int dr;
192 enum SeparatorType separatorType;
193 size_t in_len;
194 size_t out_len;
195
196 in_len = strlen(in);
197 /* See what the relative URL starts with */
198 dr = __get_drive(in, &separatorType, in_len);
199 char separator = separatorType == SeparatorTypePOSIX ? '/' : '\\';
200
201 if((separatorType != SeparatorTypeNotFound) && (separatorType == SeparatorTypeNone || in[dr] == separator)) {
202 /* It starts with "drive:" and has no separator or "drive:/", so it's already absolute */
203 out_len = snprintf(out, len, "%.*s", in_len, in);
204 } else if((separatorType != SeparatorTypeNotFound) && in[dr - 1] == ':') {
205 /* It starts with "drive:", so it's already absoulte, however it misses the "/" after unit */
206 out_len = snprintf(out, len, "%.*s%c%.*s", dr, in, separator, in_len - dr, in + dr);
207 } else if(in[0] == '\\' || in[0] == '/') {
208 /* It's absolute, but missing the drive, so use cwd's drive */
209 out_len = snprintf(out, len, "%.*s%.*s", __get_drive(__cwd, &separatorType, __cwd_len), __cwd, in_len, in);
210 } else {
211 /* It's not absolute, so append it to the current cwd */
212 out_len = snprintf(out, len, "%.*s%c%.*s", __cwd_len, __cwd, separator, in_len, in);
213 }
214 if (out_len >= len) return -1;
215
216 /* Now normalize the pathname portion */
217 dr = __get_drive(out, &separatorType, out_len);
218 return __path_normalize(out + dr, len - dr, separatorType == SeparatorTypePOSIX);
219}
220
221/* Set the current working directory (CWD) to the path where the module was launched. */
222void __init_cwd(int argc, char ** argv)
223{
224 if (argc == 0) {
225 chdir("host:");
226 return;
227 }
228
229 char * p, * s = NULL;
230 // let's find the last slash, or at worst, the :
231 for (p = argv[0]; *p; p++) {
232 if ((*p == '/') || (*p == '\\') || (*p == ':')) {
233 s = p;
234 }
235 }
236 if (s == NULL)
237 {
238 chdir("host:");
239 return;
240 }
241 char backup = *(++s);
242 *s = 0;
243 chdir(argv[0]);
244 *s = backup;
245}
#define EINVAL
Definition errno.h:72
#define ENOMEM
Definition errno.h:52
#define ERANGE
Definition errno.h:96