#include <stdlib.h>
#include <string.h>
#include "b_string.h"
#include "b_stack.h"
#include "b_path.h"
#include "b_util.h"
b_stack *b_path_new(b_string *string) {
b_stack *ret;
char *item, *dup, *tmp, *ctx = NULL;
if ((ret = b_stack_new(0)) == NULL) {
goto error_stack_new;
}
b_stack_set_destructor(ret, B_STACK_DESTRUCTOR(b_string_free));
if ((dup = malloc(string->len + 1)) == NULL) {
goto error_malloc;
}
if (memcpy(dup, string->str, string->len) == NULL) {
goto error_memcpy;
}
dup[string->len] = '\0';
tmp = dup;
while ((item = strtok_r(tmp, "/", &ctx)) != NULL) {
b_string *item_copy;
tmp = NULL;
/*
* Skip all but the first "." component for consideration.
*/
if (b_stack_count(ret) > 0 && strcmp(item, ".") == 0) {
continue;
}
/*
* Since strtok_r() discards any empty items, for convenience an empty
* string object will be added to the beginning of the stack if the input
* path is absolute.
*/
if (b_stack_count(ret) == 0 && string->str[0] == '/') {
b_stack_push(ret, b_string_new(""));
}
if ((item_copy = b_string_new(item)) == NULL) {
goto error_item_copy;
}
if (b_stack_push(ret, item_copy) == NULL) {
goto error_item_push;
}
}
/*
* If there are still no items in the stack, then add /, if the path is
* absolute. This is necessary because a path of '/', or '//' passed to
* strtok_r() would yield no results (see strtok(3) for details).
*/
if (b_stack_count(ret) == 0 && string->str[0] == '/') {
if (b_stack_push(ret, b_string_new("/")) == NULL) {
goto error_item_push;
}
}
free(dup);
return ret;
error_item_push:
error_item_copy:
error_memcpy:
free(dup);
error_malloc:
b_stack_destroy(ret);
error_stack_new:
return NULL;
}
b_string *b_path_clean(b_string *string) {
b_string *ret;
b_stack *parts;
if ((parts = b_path_new(string)) == NULL) {
goto error_path_new;
}
if ((ret = b_string_join("/", parts)) == NULL) {
goto error_join;
}
b_stack_destroy(parts);
return ret;
error_join:
b_stack_destroy(parts);
error_path_new:
return NULL;
}
b_string *b_path_clean_str(char *str) {
b_string *ret;
b_string *tmp;
if ((tmp = b_string_new(str)) == NULL) {
goto error_string_new;
}
if ((ret = b_path_clean(tmp)) == NULL) {
goto error_path_clean;
}
b_string_free(tmp);
return ret;
error_path_clean:
b_string_free(tmp);
error_string_new:
return NULL;
}