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