Example #1
0
/**
 * Normalize a path, eg, foo//bar, foo/foo2/../bar, foo/./bar all become
 * foo/bar.
 *
 * If the path begins with either a forward slash or a dot *and* a forward
 * slash, they will be preserved.
 * @param path
 * Path to normalize.
 * @return
 * The normalized path; never NULL. Must be freed.
 */
char *path_normalize(const char *path)
{
    StringBuffer *sb;
    size_t pos, startsbpos;
    char component[MAX_BUF];
    ssize_t last_slash;

    TOOLKIT_PROTECT();

    if (string_isempty(path)) {
        return estrdup(".");
    }

    sb = stringbuffer_new();
    pos = 0;

    if (string_startswith(path, "/")) {
        stringbuffer_append_string(sb, "/");
    } else if (string_startswith(path, "./")) {
        stringbuffer_append_string(sb, "./");
    }

    startsbpos = stringbuffer_length(sb);

    while (string_get_word(path, &pos, '/', component, sizeof(component), 0)) {
        if (strcmp(component, ".") == 0) {
            continue;
        }

        if (strcmp(component, "..") == 0) {
            if (stringbuffer_length(sb) > startsbpos) {
                last_slash = stringbuffer_rindex(sb, '/');

                if (last_slash == -1) {
                    LOG(BUG, "Should have found a forward slash, but didn't: %s", path);
                    continue;
                }

                stringbuffer_seek(sb, last_slash);
            }
        } else {
            size_t len = stringbuffer_length(sb);
            if (len == 0 || stringbuffer_data(sb)[len - 1] != '/') {
                stringbuffer_append_string(sb, "/");
            }

            stringbuffer_append_string(sb, component);
        }
    }

    if (stringbuffer_length(sb) == 0) {
        stringbuffer_append_string(sb, ".");
    }

    return stringbuffer_finish(sb);
}
Example #2
0
/**
 * Get absolute path to the specified relative path. This path will typically
 * point to the to client data directory (which is usually located in the user's
 * home/appdata directory), but depending on the specified mode, extra actions
 * may be performed. These ensure that if you're trying to access a file that
 * does not yet exist in the client data directory, it will be read from the
 * client installation directory instead (unless it's being appended to, in
 * which case it will be copied to the client data directory first).
 *
 * Generally, you should almost always use this when you need to construct a
 * path, or use one of the many @ref file_wrapper_functions.
 * @param fname
 * The file path.
 * @param mode
 * File mode.
 * @return
 * The absolute path. Must be freed.
 */
char *file_path(const char *path, const char *mode)
{
    bool is_write, is_append;
    StringBuffer *sb;
    char version[MAX_BUF], client_path[HUGE_BUF], *new_path;

    HARD_ASSERT(path != NULL);
    HARD_ASSERT(mode != NULL);

    SOFT_ASSERT_RC(path[0] != '/', estrdup(path),
            "Path is already absolute: %s", path);

    sb = stringbuffer_new();
    stringbuffer_append_printf(sb, "%s/.atrinik/%s/%s", get_config_dir(),
            package_get_version_partial(VS(version)), path);
    new_path = stringbuffer_sub(sb, 0, 0);

    is_write = is_append = false;

    if (strchr(mode, 'w') != NULL) {
        is_write = true;
    } else if (strchr(mode, '+') != NULL || strchr(mode, 'a') != NULL) {
        is_append = true;
    }

    if (is_write || is_append) {
        if (access(new_path, W_OK) != 0) {
            char *dirname;

            /* Ensure directories exist if we're going to use this path for
             * writing/appending. */
            dirname = path_dirname(new_path);
            mkdir_recurse(dirname);
            efree(dirname);

            if (is_append) {
                get_data_dir_file(VS(client_path), path);
                copy_file(client_path, new_path);
            }
        }
    } else {
        if (access(new_path, R_OK) != 0) {
            get_data_dir_file(VS(client_path), path);
            stringbuffer_seek(sb, 0);
            stringbuffer_append_string(sb, client_path);
        }
    }

    efree(new_path);

    return stringbuffer_finish(sb);
}