1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
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 // -------------------------------------------------------------------------*/
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
,
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
;
46 sjme_jint len
, rem
, totalCount
, frag
;
47 sjme_lpcstr rootBase
, rootEnd
;
48 sjme_lpcstr nameBase
, nameEnd
;
51 if (inPath
== NULL
|| (outBase
== NULL
&& outBaseDx
== NULL
&&
52 outEnd
== NULL
&& outEndDx
== NULL
&& outLen
== 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
;
61 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
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. */
73 stop
= &inPath
[inPathLen
];
75 /* Initially clear the total count. */
78 /* Check for the root component at the start of the path. */
81 for (at
= &inPath
[0], end
= at
; end
<= stop
;)
83 /* How long is the current path? What is left of it? */
87 #if 0 && defined(SJME_CONFIG_DEBUG)
89 sjme_message("Root look: %c", at
[0]);
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] == ':')
100 /* Not possible, stop early. */
104 #elif SJME_CONFIG_PATH_STYLE == SJME_CONFIG_PATH_STYLE_UNIX
105 /* Does this start with slash? */
106 if (len
>= 1 && at
[0] == '/')
109 if (rem
> 0 && at
[1] == '/')
125 /* Not possible, stop early. */
129 return sjme_error_notImplemented(0);
132 /* Did not find, increment up. */
136 #if defined(SJME_CONFIG_DEBUG)
138 sjme_message("Root path found: `%.*s` <- `%s`",
139 (int)(rootEnd
- rootBase
), rootBase
, inPath
);
142 /* Did we want the root component? */
145 /* There was none. */
146 if (rootBase
== NULL
|| rootEnd
== NULL
)
147 return SJME_ERROR_NO_SUCH_ELEMENT
;
149 /* Calculate outputs. */
152 if (outBaseDx
!= NULL
)
153 *outBaseDx
= rootBase
- inPath
;
156 if (outEndDx
!= NULL
)
157 *outEndDx
= rootEnd
- inPath
;
159 *outLen
= rootEnd
- rootBase
;
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
;
172 for (base
= at
, end
= at
; end
<= stop
;)
174 /* How long is the current path? What is left of it? */
179 #if 0 && defined(SJME_CONFIG_DEBUG)
181 sjme_message("Name look: %c", end
[0]);
184 /* Force hit on NUL? */
191 /* Otherwise check directory character. */
194 #if SJME_CONFIG_PATH_STYLE == SJME_CONFIG_PATH_STYLE_DOS
195 /* Directory specifier? */
196 hit
= (end
[0] == '/' || end
[0] == '\\');
199 #elif SJME_CONFIG_PATH_STYLE == SJME_CONFIG_PATH_STYLE_UNIX
200 /* Directory specifier? */
201 hit
= (end
[0] == '/');
204 return sjme_error_notImplemented(0);
208 /* Did we hit a directory split? */
213 return SJME_ERROR_ILLEGAL_STATE
;
215 /* Determine name locations. */
219 #if defined(SJME_CONFIG_DEBUG)
221 sjme_message("Name found %d: `%.*s` <- `%s`",
222 totalCount
, (int)(nameEnd
- nameBase
), nameBase
,
226 /* Is this the one we want? */
227 if (inName
== totalCount
)
229 /* Calculate outputs. */
232 if (outBaseDx
!= NULL
)
233 *outBaseDx
= nameBase
- inPath
;
236 if (outEndDx
!= NULL
)
237 *outEndDx
= nameEnd
- inPath
;
239 *outLen
= nameEnd
- nameBase
;
242 return SJME_ERROR_NONE
;
245 /* Count up and set new pointer regions. */
251 /* Do not let normal followup run. */
255 /* Did not find, increment up. */
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
;
273 if (inPath
== NULL
|| outCount
== NULL
)
274 return SJME_ERROR_NULL_ARGUMENTS
;
277 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
279 /* Use the pathname get to determine the root count. */
281 if (sjme_error_is(error
= sjme_path_getNameF(inPath
, inPathLen
,
282 INT32_MAX
, NULL
, NULL
,
283 NULL
, NULL
, NULL
, &result
)) ||
285 return sjme_error_default(error
);
287 /* Give the count. */
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
;
300 if (inPath
== NULL
|| hasRoot
== NULL
)
301 return SJME_ERROR_NULL_ARGUMENTS
;
304 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
306 /* Try to get the root. */
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
;
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
;
339 sjme_jint resultBytes
;
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
;
351 return SJME_ERROR_NONE
;
353 /* Need to check how many characters to potentially add. */
354 outLen
= strlen(outPath
);
355 subLen
= strlen(subPath
);
358 if (subPathLen
== INT32_MAX
)
361 /* Otherwise limit accordingly. */
362 else if (subLen
> subPathLen
)
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? */
376 if (sjme_error_is(error
= sjme_path_getNameCount(subPath
,
377 subPathLen
, &subNames
)) || subNames
< 0)
382 return SJME_ERROR_NONE
;
384 /* Setup result for no-overwrite operation. */
385 resultBytes
= sizeof(*result
) * outPathLen
;
386 result
= sjme_alloca(resultBytes
);
388 return SJME_ERROR_OUT_OF_MEMORY
;
389 memset(result
, 0, resultBytes
);
390 memmove(result
, outPath
, sizeof(*result
) * outLen
);
392 /* Multiple names? */
395 /* Append individual name components. */
396 for (subName
= -1; subName
< subNames
; subName
++)
398 /* Get subcomponent to add individually. */
401 if (sjme_error_is(error
= sjme_path_getName(
403 subName
, &subBase
, &subBaseLen
)) ||
404 subBase
== NULL
|| subBaseLen
< 0)
406 /* Ignore missing root. */
407 if (subName
== -1 && error
== SJME_ERROR_NO_SUCH_ELEMENT
)
410 /* Otherwise fail. */
411 return sjme_error_default(error
);
414 /* This has a root component, so make it absolute. */
417 #if defined(SJME_CONFIG_DEBUG)
419 sjme_message("Set root: `%.*s`",
420 subBaseLen
, subBase
);
424 memset(result
, 0, sizeof(*result
) * outPathLen
);
425 memmove(result
, subBase
,
426 sizeof(*result
) * subBaseLen
);
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. */
441 /* Gets single subcomponent details. */
444 if (sjme_error_is(error
= sjme_path_getName(
446 0, &subBase
, &subBaseLen
)) ||
447 subBase
== NULL
|| subBaseLen
< 0)
448 return sjme_error_default(error
);
450 #if defined(SJME_CONFIG_DEBUG)
452 sjme_message("Append single: `%.*s`",
453 subBaseLen
, subBase
);
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
);
468 memset(&result
[outLen
], 0, sizeof(*subBase
) * subBaseLen
);
469 memmove(&result
[outLen
],
470 subBase
, sizeof(*subBase
) * subBaseLen
);
474 #if defined(SJME_CONFIG_DEBUG)
475 sjme_message("resolve(%.*s, %.*s) -> %.*s",
476 outPathLen
, outPath
, subPathLen
, subPath
,
477 (int)strlen(result
), result
);
480 /* Success! Copy resultant path. */
481 outLen
= strlen(result
) + 1;
482 memmove(outPath
, result
, sizeof(*result
) * outLen
);
483 return SJME_ERROR_NONE
;