Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

C and goto

Name: Anonymous 2012-06-19 6:00

Is it okay to use goto in C for functions like this? (It's actually C++, but I'm doing C-style development.) Is this function too long, or is it okay due to the nature of null-terminated string processing? It's been a long time since I've done C-style development. Any other recommendations (except "use Lisp or XYZ language instead")?

ssize_t normalize_path(char* restrict dest, size_t dest_max, char const* restrict p, size_t n) noexcept {
    size_t i, m, length;
    ssize_t retval;
    char const* q;
    char* buffer, * state;
    char const** parts, **new_parts;
    size_t parts_size, new_parts_size;
    char const* default_parts[32];

    // validate arguments
    if ((dest_max > (SSIZE_MAX + 1)) || (!dest && dest_max) || (!p && n)) {
        errno = EINVAL;
        return -1;
    }

    length = 0;

    if (!n) {
        // empty path, normalize to current directory
        goto normalize_curdir;
    }
       
    if (*p == '/') {
        // POSIX paths may begin with one or two slashes, but three or more
        // are treated as a single slash
        ++length; ++p; --n;
        if (n && (*p == '/')) {
            ++p; --n;
            q = strncchr(p, '/', n);
            if (!q) {
                q = p + n;
            }

            if (q == p) {
                ++length;
            }
            else {
                n -= q - p;
                p = q;
            }
        }

        // copy one or two slashes into destination buffer
        for (i = 0, m = (length < dest_max) ? length : dest_max - 1; i < m; ++i) {
            dest[i] = '/';
        }

        if (!n) {
            goto null_terminate;
        }
    }

    // create a local copy of input path for tokenization
    buffer = static_cast<char*>(malloca(n + 1));
    if (!buffer) {
        return -1;
    }

    *static_cast<char*>(mempcpy(buffer, p, n)) = '\0';

    // tokenize the local path and normalize parts into stack
    i = 0;
    retval = 0;
    state = nullptr;
    parts = default_parts;
    parts_size = sizeof(default_parts) / sizeof(default_parts[0]);

    for (q = strtok_r(buffer, path<char>::sepset, &state); q; q = strtok_r(nullptr, path<char>::sepset, &state)) {
        // skip part if curdir, pop top part off stack if pardir, and if
        // the path is absolute (length is non-zero), eat all of the
        // redundant pardir parts
        if (*q == '.') {
            if (q[1] == '\0') {
                continue;
            }
            else if ((q[1] == '.') && (q[2] == '\0')) {
                if (i > 0) {
                    --i;
                    continue;
                }
                else if (length > 0) {
                    continue;
                }
            }
        }

        // resize the path parts stack if space is exhausted
        if (i >= parts_size) {
            if (parts_size >= (SIZE_MAX / (3 * sizeof(char const*)))) {
                errno = ENOMEM;
                retval = -1;
                goto cleanup;
            }

            new_parts_size = (parts_size * 3) / 2;
            new_parts = static_cast<char const**>(malloc(new_parts_size * sizeof(char const*)));
            if (!new_parts) {
                retval = -1;
                goto cleanup;
            }

            memcpy(new_parts, parts, parts_size * sizeof(char const*));
            if (parts != default_parts) {
                free(parts);
            }

            parts = new_parts;
            parts_size = new_parts_size;
        }

        // push part onto stack
        parts[i++] = q;
    }

    // rejoin the path parts in normalized form
    if (length < dest_max) {
        retval = join_path(dest + length, dest_max - length, parts, i);
    }
    else {
        retval = join_path(nullptr, 0, parts, i);
    }

cleanup:

    // free temporary buffers
    if (parts != default_parts) {
        free(parts);
    }

    freea(buffer);

    // update length with full length from join
    if (retval < 0) {
        return -1;
    }

    length += static_cast<size_t>(retval);
    if (!length) {
        goto normalize_curdir;
    }
    else if (length > SSIZE_MAX) {
        errno = EOVERFLOW;
        return -1;
    }

    return static_cast<ssize_t>(length);

normalize_curdir:

    // empty path, normalize to current directory
    UP_ASSERT(!length);
    ++length;
    if (dest_max > 0) {
        dest[0] = '.';
    }

null_terminate:

    // null terminate the destination buffer
    if (length < dest_max) {
        dest[length] = '\0';
    }
    else if (dest_max > 0) {
        dest[dest_max - 1] = '\0';
    }

    return static_cast<ssize_t>(length);
}

Name: Anonymous 2012-06-19 7:29

I made this in 25 minutes. It's ugly but it works and is efficient.

#include <stdio.h>
#include <stdlib.h>

#define MAX_DEPTH 64

char* normalise(char* dst, const char* path)
{
        const char* path_stack[MAX_DEPTH];
        size_t path_depth = 0;
        size_t di = 0;
        size_t si = 0;
        while (*path && (path_depth < MAX_DEPTH)) {
                if ((*path == '/') || (*path == '\\'))
                        path++;
                if (!(('.' == path[0]) &&
                      (('/' == path[1]) || ('\\' == path[1])))) {
                        if (('.' == path[0]) &&
                            ('.' == path[1]) &&
                            (('/' == path[2]) || ('\\' == path[2]))) {
                                if (path_depth)
                                        path_depth--;
                                path += 3;
                        } else {
                                path_stack[path_depth] = path;
                                path_depth++;
                                do {
                                        if (*path == 0)
                                                break;
                                        path++;
                                } while ((*path != '/') && (*path != '\\'));
                        }
                } else {
                        path += 2;
                }
        }

        for (si = 0; si < path_depth; si++) {
                const char* c;
                for (c = path_stack[si];
                     (*c != '\\') && (*c != '/') && *c;
                     c++) {
                        dst[di++] = *c;
                        if (di == 255) goto out;
                }
                if (si != (path_depth - 1))
                        dst[di++] = '/';
                if (di == 255) goto out;
        }
out:
        dst[di] = 0;
}

void test(const char* path)
{
        static char norm[256];
        normalise(norm, path);
        printf("%s -> %s\n", path, norm);
}

int main()
{
        test("/foo/bar/../../../../bar/foo/./");
        test("./bar/../foo");
        test("c:\\cygwin\\home\\zun\\prog\\th11\\src\\core\\../core/zunlib.h");
        return 0;
}

Name: >>5 2012-06-19 7:43

More bugfixes.
#include <stdio.h>
#include <stdlib.h>

#define MAX_DEPTH 64

void normalise(char* dst, const char* path)
{
     const char* path_stack[MAX_DEPTH];
    size_t path_depth = 0;
    size_t di = 0;
    size_t si = 0;
    while (*path && (path_depth < MAX_DEPTH)) {
        while ((*path == '/') || (*path == '\\'))
                        path++;
        if (!(('.' == path[0]) &&
              (('/' == path[1]) || ('\\' == path[1]) || !path[1]))) {
                if (('.' == path[0]) &&
                    ('.' == path[1]) &&
                (('/' == path[2]) || ('\\' == path[2]) || !path[2])) {
                        if (path_depth)
                                    path_depth--;
                                path += 2;
                        } else {
                                path_stack[path_depth] = path;
                path_depth++;
                                do {
                                        if (*path == 0)
                                             break;
                                    path++;
                            } while ((*path != '/') && (*path != '\\'));
                        }
        } else {
                        path++;
                }
     }

    for (si = 0; si < path_depth; si++) {
            const char* c;
            for (c = path_stack[si];
                 (*c != '\\') && (*c != '/') && *c;
                 c++) {
                        dst[di++] = *c;
            if (di == 255) goto out;
        }
                if (si != (path_depth - 1))
            dst[di++] = '/';
        if (di == 255) goto out;
    }
out:
        dst[di] = 0;
}
 
void test(const char* path)
{
     static char norm[256];
    normalise(norm, path);
    printf("`%s` -> `%s`\n", path, norm);
}
 
int main()
{
     test("");
    test("/");
    test("..");
    test("/foo/bar/../../../../bar/foo/./");
    test("./bar/../foo");
    test("//c:\\cygwin\\home\\zun\\prog\\th11\\src\\core\\../core/zunlib.h");
    return 0;
}

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List