Compile fixes.
[SquirrelJME.git] / nanocoat / lib / base / path.c
blob63f7caea736929554cee275a71b2d8ba55b20010
1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
10 #include <string.h>
12 #include "sjme/path.h"
13 #include "sjme/debug.h"
15 sjme_errorCode sjme_path_getName(
16 sjme_attrInNotNull sjme_lpcstr inPath,
17 sjme_attrInPositive sjme_jint inPathLen,
18 sjme_attrInNegativeOnePositive sjme_jint inName,
19 sjme_attrOutNullable sjme_lpcstr* outBase,
20 sjme_attrOutNullable sjme_jint* outLen)
22 if (inPath == NULL || (outBase == NULL && outLen == NULL))
23 return SJME_ERROR_NULL_ARGUMENTS;
25 /* Forward to full form. */
26 return sjme_path_getNameF(inPath,
27 inPathLen, inName,
28 outBase, NULL,
29 NULL, NULL,
30 outLen, NULL);
33 sjme_errorCode sjme_path_getNameF(
34 sjme_attrInNotNull sjme_lpcstr inPath,
35 sjme_attrInPositive sjme_jint inPathLen,
36 sjme_attrInNegativeOnePositive sjme_jint inName,
37 sjme_attrOutNullable sjme_lpcstr* outBase,
38 sjme_attrOutNullable sjme_jint* outBaseDx,
39 sjme_attrOutNullable sjme_lpcstr* outEnd,
40 sjme_attrOutNullable sjme_jint* outEndDx,
41 sjme_attrOutNullable sjme_jint* outLen,
42 sjme_attrOutNullable sjme_jint* outCount)
44 sjme_lpcstr at, end, base;
45 sjme_lpcstr stop;
46 sjme_jint len, rem, totalCount, frag;
47 sjme_lpcstr rootBase, rootEnd;
48 sjme_lpcstr nameBase, nameEnd;
49 sjme_jboolean hit;
51 if (inPath == NULL || (outBase == NULL && outBaseDx == NULL &&
52 outEnd == NULL && outEndDx == NULL && outLen == NULL &&
53 outCount == NULL))
54 return SJME_ERROR_NULL_ARGUMENTS;
56 if ((outCount == NULL) != (outBase != NULL || outBaseDx != NULL ||
57 outEnd != NULL || outEndDx != NULL || outLen != NULL))
58 return SJME_ERROR_INVALID_ARGUMENT;
60 if (inPathLen < 0)
61 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
63 if (inName < -1)
64 return SJME_ERROR_INVALID_ARGUMENT;
66 if (outCount != NULL && inName != INT32_MAX)
67 return SJME_ERROR_INVALID_ARGUMENT;
69 /* Stopping point is always here. */
70 len = strlen(inPath);
71 if (len < inPathLen)
72 inPathLen = len;
73 stop = &inPath[inPathLen];
75 /* Initially clear the total count. */
76 totalCount = 0;
78 /* Check for the root component at the start of the path. */
79 rootBase = NULL;
80 rootEnd = NULL;
81 for (at = &inPath[0], end = at; end <= stop;)
83 /* How long is the current path? What is left of it? */
84 len = end - at;
85 rem = stop - end;
87 #if 0 && defined(SJME_CONFIG_DEBUG)
88 /* Debug. */
89 sjme_message("Root look: %c", at[0]);
90 #endif
92 #if SJME_CONFIG_PATH_STYLE == SJME_CONFIG_PATH_STYLE_DOS
93 /* Does this look like a drive letter? */
94 if (len >= 2 && (((at[0]) >= 'a' && (at[0]) <= 'z') ||
95 ((at[0]) >= 'A' && (at[0]) <= 'Z')) && at[1] == ':')
97 sjme_todo("Impl?");
100 /* Not possible, stop early. */
101 if (len >= 3)
102 break;
104 #elif SJME_CONFIG_PATH_STYLE == SJME_CONFIG_PATH_STYLE_UNIX
105 /* Does this start with slash? */
106 if (len >= 1 && at[0] == '/')
108 /* Double slash? */
109 if (rem > 0 && at[1] == '/')
111 rootBase = &at[0];
112 rootEnd = &at[2];
113 break;
116 /* Single slash. */
117 else
119 rootBase = &at[0];
120 rootEnd = &at[1];
121 break;
125 /* Not possible, stop early. */
126 if (len >= 3)
127 break;
128 #else
129 return sjme_error_notImplemented(0);
130 #endif
132 /* Did not find, increment up. */
133 end++;
136 #if defined(SJME_CONFIG_DEBUG)
137 /* Debug. */
138 sjme_message("Root path found: `%.*s` <- `%s`",
139 (int)(rootEnd - rootBase), rootBase, inPath);
140 #endif
142 /* Did we want the root component? */
143 if (inName == -1)
145 /* There was none. */
146 if (rootBase == NULL || rootEnd == NULL)
147 return SJME_ERROR_NO_SUCH_ELEMENT;
149 /* Calculate outputs. */
150 if (outBase != NULL)
151 *outBase = rootBase;
152 if (outBaseDx != NULL)
153 *outBaseDx = rootBase - inPath;
154 if (outEnd != NULL)
155 *outEnd = rootEnd;
156 if (outEndDx != NULL)
157 *outEndDx = rootEnd - inPath;
158 if (outLen != NULL)
159 *outLen = rootEnd - rootBase;
161 /* Success! */
162 return SJME_ERROR_NONE;
165 /* Start from the path unless a root was specified. */
166 at = (rootEnd != NULL ? rootEnd : &inPath[0]);
168 /* Scan through remaining components. */
169 hit = SJME_JNI_FALSE;
170 nameBase = NULL;
171 nameEnd = NULL;
172 for (base = at, end = at; end <= stop;)
174 /* How long is the current path? What is left of it? */
175 len = end - at;
176 rem = stop - end;
177 frag = -1;
179 #if 0 && defined(SJME_CONFIG_DEBUG)
180 /* Debug. */
181 sjme_message("Name look: %c", end[0]);
182 #endif
184 /* Force hit on NUL? */
185 if (end[0] == '\0')
187 hit = SJME_JNI_TRUE;
188 frag = 1;
191 /* Otherwise check directory character. */
192 else
194 #if SJME_CONFIG_PATH_STYLE == SJME_CONFIG_PATH_STYLE_DOS
195 /* Directory specifier? */
196 hit = (end[0] == '/' || end[0] == '\\');
197 frag = 1;
199 #elif SJME_CONFIG_PATH_STYLE == SJME_CONFIG_PATH_STYLE_UNIX
200 /* Directory specifier? */
201 hit = (end[0] == '/');
202 frag = 1;
203 #else
204 return sjme_error_notImplemented(0);
205 #endif
208 /* Did we hit a directory split? */
209 if (hit)
211 /* Should be set. */
212 if (frag <= 0)
213 return SJME_ERROR_ILLEGAL_STATE;
215 /* Determine name locations. */
216 nameBase = base;
217 nameEnd = end;
219 #if defined(SJME_CONFIG_DEBUG)
220 /* Debug. */
221 sjme_message("Name found %d: `%.*s` <- `%s`",
222 totalCount, (int)(nameEnd - nameBase), nameBase,
223 inPath);
224 #endif
226 /* Is this the one we want? */
227 if (inName == totalCount)
229 /* Calculate outputs. */
230 if (outBase != NULL)
231 *outBase = nameBase;
232 if (outBaseDx != NULL)
233 *outBaseDx = nameBase - inPath;
234 if (outEnd != NULL)
235 *outEnd = nameEnd;
236 if (outEndDx != NULL)
237 *outEndDx = nameEnd - inPath;
238 if (outLen != NULL)
239 *outLen = nameEnd - nameBase;
241 /* Success! */
242 return SJME_ERROR_NONE;
245 /* Count up and set new pointer regions. */
246 totalCount++;
247 base = &end[frag];
248 at = base;
249 end = base;
251 /* Do not let normal followup run. */
252 continue;
255 /* Did not find, increment up. */
256 end++;
259 /* Success! */
260 if (outCount != NULL)
261 *outCount = totalCount;
262 return SJME_ERROR_NONE;
265 sjme_errorCode sjme_path_getNameCount(
266 sjme_attrInNotNull sjme_lpcstr inPath,
267 sjme_attrInPositive sjme_jint inPathLen,
268 sjme_attrOutNotNull sjme_attrOutPositive sjme_jint* outCount)
270 sjme_errorCode error;
271 sjme_jint result;
273 if (inPath == NULL || outCount == NULL)
274 return SJME_ERROR_NULL_ARGUMENTS;
276 if (inPathLen < 0)
277 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
279 /* Use the pathname get to determine the root count. */
280 result = -1;
281 if (sjme_error_is(error = sjme_path_getNameF(inPath, inPathLen,
282 INT32_MAX, NULL, NULL,
283 NULL, NULL, NULL, &result)) ||
284 result < 0)
285 return sjme_error_default(error);
287 /* Give the count. */
288 *outCount = result;
289 return SJME_ERROR_NONE;
292 sjme_errorCode sjme_path_hasRoot(
293 sjme_attrInNotNull sjme_lpcstr inPath,
294 sjme_attrInPositive sjme_jint inPathLen,
295 sjme_attrOutNotNull sjme_jboolean* hasRoot)
297 sjme_errorCode error;
298 sjme_jint len;
300 if (inPath == NULL || hasRoot == NULL)
301 return SJME_ERROR_NULL_ARGUMENTS;
303 if (inPathLen < 0)
304 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
306 /* Try to get the root. */
307 len = -1;
308 if (sjme_error_is(error = sjme_path_getNameF(inPath, inPathLen,
310 NULL, NULL, NULL, NULL,
311 &len, NULL)) || len <= 0)
313 /* Does not have one? */
314 if (error == SJME_ERROR_NO_SUCH_ELEMENT)
316 *hasRoot = SJME_JNI_TRUE;
317 return SJME_ERROR_NONE;
320 /* Other failure. */
321 return sjme_error_default(error);
324 /* If this was reached, there is none. */
325 *hasRoot = SJME_JNI_FALSE;
326 return SJME_ERROR_NONE;
329 sjme_errorCode sjme_path_resolveAppend(
330 sjme_attrOutNotNull sjme_lpstr outPath,
331 sjme_attrInPositiveNonZero sjme_jint outPathLen,
332 sjme_attrInNotNull sjme_lpcstr subPath,
333 sjme_attrInPositiveNonZero sjme_jint subPathLen)
335 sjme_errorCode error;
336 sjme_jint outLen, subLen;
337 sjme_jint subNames, subName;
338 sjme_lpstr result;
339 sjme_jint resultBytes;
340 sjme_lpcstr subBase;
341 sjme_jint subBaseLen, sepLen;
343 if (outPath == NULL || subPath == NULL)
344 return SJME_ERROR_NULL_ARGUMENTS;
346 if (outPathLen <= 0 || subBaseLen < 0)
347 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
349 /* Nothing to do? */
350 if (subBaseLen == 0)
351 return SJME_ERROR_NONE;
353 /* Need to check how many characters to potentially add. */
354 outLen = strlen(outPath);
355 subLen = strlen(subPath);
357 /* Special? */
358 if (subPathLen == INT32_MAX)
359 subPathLen = subLen;
361 /* Otherwise limit accordingly. */
362 else if (subLen > subPathLen)
363 subLen = subPathLen;
365 /* Nothing to do? */
366 if (subLen == 0)
367 return SJME_ERROR_NONE;
369 /* Pre-determine if this will overflow. */
370 if (outLen < 0 || subLen < 0 || (outLen + subLen) < 0 ||
371 (outLen + subLen) + 1 > outPathLen)
372 return SJME_ERROR_PATH_TOO_LONG;
374 /* How many names does the sub-path have? */
375 subNames = -1;
376 if (sjme_error_is(error = sjme_path_getNameCount(subPath,
377 subPathLen, &subNames)) || subNames < 0)
378 return error;
380 /* Pointless? */
381 if (subNames == 0)
382 return SJME_ERROR_NONE;
384 /* Setup result for no-overwrite operation. */
385 resultBytes = sizeof(*result) * outPathLen;
386 result = sjme_alloca(resultBytes);
387 if (result == NULL)
388 return SJME_ERROR_OUT_OF_MEMORY;
389 memset(result, 0, resultBytes);
390 memmove(result, outPath, sizeof(*result) * outLen);
392 /* Multiple names? */
393 if (subNames > 1)
395 /* Append individual name components. */
396 for (subName = -1; subName < subNames; subName++)
398 /* Get subcomponent to add individually. */
399 subBase = NULL;
400 subBaseLen = -1;
401 if (sjme_error_is(error = sjme_path_getName(
402 subPath, subPathLen,
403 subName, &subBase, &subBaseLen)) ||
404 subBase == NULL || subBaseLen < 0)
406 /* Ignore missing root. */
407 if (subName == -1 && error == SJME_ERROR_NO_SUCH_ELEMENT)
408 continue;
410 /* Otherwise fail. */
411 return sjme_error_default(error);
414 /* This has a root component, so make it absolute. */
415 if (subName == -1)
417 #if defined(SJME_CONFIG_DEBUG)
418 /* Debug. */
419 sjme_message("Set root: `%.*s`",
420 subBaseLen, subBase);
421 #endif
423 /* Copy over. */
424 memset(result, 0, sizeof(*result) * outPathLen);
425 memmove(result, subBase,
426 sizeof(*result) * subBaseLen);
428 continue;
431 /* Append individual path. */
432 if (sjme_error_is(error = sjme_path_resolveAppend(result,
433 outPathLen, subBase, subBaseLen)))
434 return sjme_error_default(error);
438 /* Single path only. */
439 else
441 /* Gets single subcomponent details. */
442 subBase = NULL;
443 subBaseLen = -1;
444 if (sjme_error_is(error = sjme_path_getName(
445 subPath, subPathLen,
446 0, &subBase, &subBaseLen)) ||
447 subBase == NULL || subBaseLen < 0)
448 return sjme_error_default(error);
450 #if defined(SJME_CONFIG_DEBUG)
451 /* Debug. */
452 sjme_message("Append single: `%.*s`",
453 subBaseLen, subBase);
454 #endif
456 /* Recalculate output length. */
457 outLen = strlen(result);
459 /* Add directory separator. */
460 sepLen = strlen(SJME_CONFIG_FILE_SEPARATOR) + 1;
461 memmove(&result[outLen], SJME_CONFIG_FILE_SEPARATOR,
462 sizeof(*result) * sepLen);
464 /* Recalculate output length. */
465 outLen = strlen(result);
467 /* Append it. */
468 memset(&result[outLen], 0, sizeof(*subBase) * subBaseLen);
469 memmove(&result[outLen],
470 subBase, sizeof(*subBase) * subBaseLen);
473 /* Debug. */
474 #if defined(SJME_CONFIG_DEBUG)
475 sjme_message("resolve(%.*s, %.*s) -> %.*s",
476 outPathLen, outPath, subPathLen, subPath,
477 (int)strlen(result), result);
478 #endif
480 /* Success! Copy resultant path. */
481 outLen = strlen(result) + 1;
482 memmove(outPath, result, sizeof(*result) * outLen);
483 return SJME_ERROR_NONE;