diff options
Diffstat (limited to 'lib/qpath.c')
-rw-r--r-- | lib/qpath.c | 785 |
1 files changed, 589 insertions, 196 deletions
diff --git a/lib/qpath.c b/lib/qpath.c index 07c294de..e3f690a6 100644 --- a/lib/qpath.c +++ b/lib/qpath.c @@ -24,6 +24,9 @@ #include "zassert.h" +#include <unistd.h> +#include <errno.h> + /*============================================================================== * Some primitive path handling, based on qstrings. * @@ -43,35 +46,40 @@ * */ +static void qpath_reduce(qpath qp) ; + /*------------------------------------------------------------------------------ * Initialise a brand new qpath -- allocate if required. * - * If a path is given, set that path -- allocating body even if path is zero - * length. - * - * If no path is given, leaves qpath with no body. - * - * If path is given, the qpath is set and reduced (see above). + * The result is a path with a not-empty body. All qpath values may be assumed + * to have a body at all times. * * Returns: address of qpath * * NB: assumes initialising a new structure. If not, then caller should - * use qpath_reset() or qs_clear(). + * use qpath_reset() or qpath_clear(). */ extern qpath -qpath_init_new(qpath qp, const char* path) +qpath_init_new(qpath qp) { if (qp == NULL) qp = XCALLOC(MTYPE_QPATH, sizeof(qpath_t)) ; else memset(qp, 0, sizeof(qpath_t)) ; - /* Worry about fields other than the path */ + qs_init_new(qp->path, 50) ; /* Always have a body */ - qs_init_new(qp->path, 0) ; + return qp ; +} ; - if (path != NULL) - qpath_set(qp, path) ; +/*------------------------------------------------------------------------------ + * Create a new qpath if don't already have one. + */ +inline static qpath +qpath_make_if_null(qpath qp) +{ + if (qp == NULL) + qp = qpath_init_new(NULL) ; return qp ; } ; @@ -82,172 +90,251 @@ qpath_init_new(qpath qp, const char* path) * Discards all the contents of the qpath. */ extern qpath -qpath_reset(qpath qp, bool free_structure) +qpath_reset(qpath qp, free_keep_b free_structure) { if (qp == NULL) return NULL ; - qs_reset_keep(&qp->path, keep_it) ; + qs_reset(qp->path, keep_it) ; if (free_structure) XFREE(MTYPE_QPATH, qp) ; /* sets qp = NULL */ else - /* Worry about fields other than the path */ ; return qp ; } ; /*------------------------------------------------------------------------------ - * Set given qpath to copy of the given string -- allocate if required. - * - * If setting an existing qpath, discards any existing contents -- so the qpath - * MUST have been initialised at some time (qpath_init_new). Keeps any body - * that has been allocated if possible. - * - * Reduces the path (see above). - * - * Sets the path len, but does not touch the path cp. + * Clear down given qpath -- retaining any body, but setting it empty. */ extern qpath -qpath_set(qpath qp, const char* path) +qpath_clear(qpath qp) { if (qp == NULL) - qp = qpath_init_new(NULL, path) ; - else - { - if (path != NULL) - qs_set(&qp->path, path) ; - else - qs_clear(qp->path) ; - /* Worry about fields other than the path */ - } ; + return NULL ; - qpath_reduce(qp) ; + qs_clear(qp->path) ; return qp ; } ; /*------------------------------------------------------------------------------ - * Reduce multiple '/' to single '/' (except for exactly "//" at start). + * Set given qpath to copy of the given string -- allocate if required. * - * Reduce "/./" to "/". + * Reduces the path (see above). */ -static void -qpath_reduce(qpath qp, size_t off) +extern qpath +qpath_set(qpath dst, const char* src) { - qpath part ; - qstring qs ; - char* sp ; - char* p ; - char* q ; + return qpath_set_n(dst, src, (src != NULL) ? strlen(src) : 0) ; +} ; - if (qp == NULL) - { - assert(off == 0) ; - return ; /* NULL qpath is empty */ - } ; +/*------------------------------------------------------------------------------ + * Set given qpath to copy of the given qstring -- allocate if required. + * + * Reduces the path (see above). + */ +extern qpath +qpath_set_qs(qpath dst, const qstring src) +{ + return qpath_set_n(dst, qs_char(src), qs_len(src)) ; +} ; - qs = &qp->path ; - assert(off <= qs->len) ; /* Make sure 'off' is kosher */ +/*------------------------------------------------------------------------------ + * Set given qpath to copy of the given string -- allocate if required. + * + * Reduces the path (see above). + */ +extern qpath +qpath_set_n(qpath dst, const char* src, ulen n) +{ + dst = qpath_make_if_null(dst) ; - sp = qs_chars(qs) ; /* NULL if qpath is completely empty */ + qs_set_n(dst->path, src, n) ; - if (sp == NULL) - return ; /* NULL path part is completely empty */ + qpath_reduce(dst) ; - p = sp + off ; + return dst ; +} ; - /* Deal with special case of "//" at start. - * - * If find "//x", where x is anything other than '/', step past the first - * '/'. Could step past both "//", but that stops it seeing "//./zzz" - */ - if ((*p == '/') && (*(p + 1) == '/') && (*(p + 2) != '/')) - ++p ; +/*------------------------------------------------------------------------------ + * Copy qpath to the given qpath -- creating qpath if required. + * The result is an empty qpath if the given one is NULL. + */ +extern qpath +qpath_copy(qpath dst, const qpath src) +{ + if (src != NULL) + return qpath_set_n(dst, qs_char_nn(src->path), qs_len_nn(src->path)) ; + else + return qpath_set_n(dst, NULL, 0) ; +} ; - /* Scan to see if there is anything that needs to be fixed. - * - * Looking for "//" and "/./". - */ - while (1) - { - if (*p++ == '\0') - return ; /* nothing to do if hit end of string */ +/*============================================================================== + * Interfaces to system. + */ - if ( (*p == '/') || ((*p == '.') && (*(p + 1) == '/')) ) - { - if (*(p - 1) == '/') - break ; /* found "//" or "/./" */ - } - } ; +/*------------------------------------------------------------------------------ + * Get the current working directory -- creates a qpath if required. + * + * Returns: the (new) qpath if OK + * NULL if not OK -- any existing qpath is cleared. + * + * If fails will be because some directory on the way back to the root is + * not readable or searchable (!). + */ +extern qpath +qpath_getcwd(qpath dst) +{ + qpath od ; - /* Rats... there is something to be fixed. - * - * *p is second '/' of "//" or '.' of "/./". - */ - q = p ; + od = dst ; + dst = qpath_make_if_null(dst) ; - while (*p != '\0') + qs_new_size(dst->path, 50) ; + + while (1) { - /* Step past any number of '/' and any number of "./". */ - while (1) - { - while (*p == '/') - ++p ; + void* r ; + usize s ; - if ((*p != '.') || (*p != '/')) - break ; + s = qs_size_nn(dst->path) ; + r = getcwd(qs_char_nn(dst->path), s) ; - p += 2 ; + if (r != NULL) + { + qs_set_strlen_nn(dst->path) ; + return dst ; /* exit here if OK. */ } ; - /* Scan, copying stuff, until get to '\0' or find "//" or "/./" */ - while (*p != '\0') - { - *q++ = *p++ ; /* copy non-'\0' */ + if (errno != ERANGE) + break ; /* exit here on failure */ - if ( (*p == '/') || ((*p == '.') && (*(p + 1) == '/')) ) - { - if (*(p - 1) == '/') - break ; /* found "//" or "/./" */ - } ; - } ; + qs_new_size(dst->path, s * 2) ; } ; - /* Adjust the length and terminate */ + if (od == NULL) + qpath_reset(dst, free_it) ; + else + qpath_clear(dst) ; - qs->len = (q - sp) ; /* set the new length (shorter !) */ - *q = '\0' ; /* and terminate */ + return NULL ; } ; /*------------------------------------------------------------------------------ - * Make a copy of the given qpath. + * Set the current working directory * - * Creates a brand new qpath object, which is a full copy of the given one. + * Returns: 0 <=> OK + * errno otherwise + */ +extern int +qpath_setcwd(qpath qp) +{ + return (chdir(qpath_string(qp)) == 0) ? 0 : errno ; +} ; + +/*------------------------------------------------------------------------------ + * Do "stat" for given path. * - * The result is an empty qpath if the given one is NULL. + * Returns: 0 <=> OK + * errno otherwise */ -extern qpath -qpath_copy(qpath qp_x) +extern int +qpath_stat(qpath qp, struct stat* sbuf) { - return qpath_init_new(NULL, qpath_path(qp_x)) ; + return (stat(qpath_string(qp), sbuf) == 0) ? 0 : errno ; } ; /*------------------------------------------------------------------------------ - * Make a copy of a qpath to the given qpath. + * Is given path a file we might access -- according to stat ? * - * If required, creates new qpath object -- so qpath_copy_to(NULL, ...) is the - * same as qpath_copy(...). + * Returns: -1 <=> no -- can access etc, but it's not a file + * 0 <=> yes + * errno otherwise + */ +extern int +qpath_stat_is_file(qpath qp) +{ + struct stat sbuf[1] ; + int err ; + + err = qpath_stat(qp, sbuf) ; + + return (err == 0) ? (S_ISREG(sbuf->st_mode) ? 0 : -1) + : err ; +} ; + +/*------------------------------------------------------------------------------ + * Is given path a directory we might access -- according to stat ? * - * The result is an empty qpath if the given one is NULL. + * Returns: -1 <=> no -- can access etc, but it's not a directory + * 0 <=> yes + * errno otherwise + */ +extern int +qpath_stat_is_directory(qpath qp) +{ + struct stat sbuf[1] ; + int err ; + + err = qpath_stat(qp, sbuf) ; + + return (err == 0) ? (S_ISDIR(sbuf->st_mode) ? 0 : -1) + : err ; +} ; + +/*============================================================================== + * Path editing functions + * + * + */ + +/*------------------------------------------------------------------------------ + * Shave any file part off the end of the given path. + * + * This treats anything after the last '/' of the path as being the "file part". + * + * The cases are (where 'a' is anything except '/' and 'z' is anything): + * + * 1. "" -- empty -> unchanged + * + * 2. "aaa" -- file part only -> "" + * + * 3. "/" -- root only, or -> unchanged + * "//" double root only -> unchanged + * + * 4. "/aaa" -- routed file -> "/" + * "//aaa" double routed file -> "//" + * + * 5. "zzz/" -- no file part -> unchanged + * + * 6. "zzz/aaa" -- non-empty file part -> "zzz/" + * + * Ensures that the path is "reduced" before shaving. + * + * Creates a new, empty path if qp is NULL. */ extern qpath -qpath_copy_to(qpath qp, qpath qp_x) +qpath_shave(qpath qp) { - return qpath_set(qp, qpath_path(qp_x)) ; + pp_t p ; + + qp = qpath_make_if_null(qp) ; + + qs_pp_nn(p, qp->path) ; + + /* Track back to last '/' */ + while ( (p->e > p->p) && (*(p->e - 1) != '/') ) + --p->e ; + + qs_set_len_nn(qp->path, p->e - p->p) ; + + return qp ; } ; +#if 0 + /*============================================================================== * Pop the last part of the given path. * @@ -291,7 +378,7 @@ qpath_copy_to(qpath qp, qpath qp_x) * Note that other forms of multiple '/' have been reduced, already. */ extern qpath -qpath_pop(qpath qp) +qpath_pop(qpath to, qpath from) { qpath part ; qstring qs ; @@ -348,6 +435,9 @@ qpath_pop(qpath qp) return part ; } ; + + + /*============================================================================== * Shift off the first part of the given path. * @@ -444,60 +534,85 @@ qpath_shift(qpath qp) /* Return the part we hacked off */ return part ; } ; +#endif + +/*============================================================================== + * Append, Prepend and Complete + */ + +static ulen qpath_trim_home(const char* p, ulen n) ; /*------------------------------------------------------------------------------ - * Push one path onto the end of the given path. + * Append one path (src) onto the end of the given path (dst). * - * If the given path is NULL, creates a new, empty qpath to push onto. + * If the dst path is NULL, creates a new, empty qpath to append to. * - * The given path is assumed to be the path to a "directory". An empty - * given path is treated as "the current directory". + * The dst path is assumed to be the path to a "directory". An empty dst path + * is treated as "the current directory". * - * If the path to be pushed starts '/' or '~', then it is trimmed, removing - * leading characters upto and including '/' (stopping at '\0' if no '/' found). + * If src path starts '/' or '~', then it is trimmed, removing leading + * characters up to and including '/'. * - * If path to be pushed onto is not empty, and does not end '/', then an '/' - * is appended before the path is pushed. + * If dst path is not empty, and does not end '/', then an '/' is appended + * before the src is appended. * * Note that this means: * - * -- pushing an empty path or one which is just "/", will leave the path - * ending "/" -- unless the given path is empty. + * -- appending an empty path or one which is just "/", will leave the dst + * path ending "/" -- unless the dst path is empty. * - * -- cannot create a rooted path by pushing a path onto an empty path. + * -- cannot create a rooted path by appending a path onto an empty path. * - * -- pushing a "homed" path "~...." is assumed to be pushing onto the + * -- appending a "homed" path "~..../" is assumed to be appending to the * required "home". * * The resulting path is reduced (see above). */ +extern qpath +qpath_append(qpath dst, const qpath src) +{ + if (src != NULL) + return qpath_append_str_n(dst, qs_char_nn(src->path), + qs_len_nn(src->path)) ; + else + return qpath_append_str_n(dst, NULL, 0) ; +} ; +/*------------------------------------------------------------------------------ + * Append src qstring onto the end of the dst path. + * + * See above for discussion of "append" operation. + */ extern qpath -qpath_push(qpath qp, qpath qp_a) +qpath_append_qs(qpath dst, const qstring src) { - return qpath_push_str(qp, qpath_path(qp_a)) ; + return qpath_append_str_n(dst, qs_char(src), qs_len(src)) ; } ; /*------------------------------------------------------------------------------ - * Push path string onto the end of the given path. + * Append src string onto the end of the dst path. * - * See above for discussion of "push" operation. + * See above for discussion of "append" operation. */ extern qpath -qpath_push_str(qpath qp, const char* path) +qpath_append_str(qpath dst, const char* src) { - qstring qs ; - char* ep ; - char* sp ; - size_t len ; - size_t off ; + return qpath_append_str_n(dst, src, (src != NULL) ? strlen(src) : 0) ; +} ; - if (qp == NULL) - qp = qpath_init_new(NULL, NULL) ; +/*------------------------------------------------------------------------------ + * Append src string of given length onto the end of the dst path. + * + * See above for discussion of "append" operation. + */ +extern qpath +qpath_append_str_n(qpath dst, const char* src, ulen n) +{ + ulen l ; - qs = &qp->path ; + dst = qpath_make_if_null(dst) ; - /* Trim the path to be pushed: + /* Trim the path to be appended: * * 1. discard from any leading '~' to the first '/' or to '\0'. * @@ -505,87 +620,257 @@ qpath_push_str(qpath qp, const char* path) * * 3. then establish length of result. */ - if (path != NULL) + l = n ; + n = qpath_trim_home(src, n) ; + src += (l - n) ; /* step past stuff trimmed */ + + /* Worry about whether need to add a '/' to the path before pushing */ + if (qs_len_nn(dst->path) != 0) { - if (*path == '~') - do - { - ++path ; - } while ((*path != '/') && (*path != '\0')) ; - - while (*path == '/') - ++path ; /* Step past leading '/' */ - len = strlen(path) ; - } + if (*(qs_ep_char_nn(dst->path) - 1) != '/') + qs_append_str_n(dst->path, "/", 1) ; + } ; + + /* Now append the src */ + qs_append_str_n(dst->path, src, n) ; + + /* Reduce the new part of the result, and return */ + qpath_reduce(dst) ; + + return dst ; +} ; + +/*------------------------------------------------------------------------------ + * Extend given dst path by simply affixing the given src path. + * + * Does not introduce any '/' or any other stuff. + * + * The resulting path is reduced (see above). + */ +extern qpath +qpath_extend(qpath dst, const qpath src) +{ + if (src != NULL) + return qpath_extend_str_n(dst, qs_char_nn(src->path), + qs_len_nn(src->path)) ; else - len = 0 ; + return qpath_extend_str_n(dst, NULL, 0) ; +} ; - /* Worry about whether need to add a '/' to the path before pushing */ - sp = qs_char(qs) ; - ep = qs_ep_char(qs) ; /* points at trailing '\0' */ +/*------------------------------------------------------------------------------ + * Extend given dst path by simply affixing the given src qstring. + */ +extern qpath +qpath_extend_qs(qpath dst, const qstring src) +{ + return qpath_extend_str_n(dst, qs_char(src), qs_len(src)) ; +} ; - if (sp == NULL) - assert(ep == sp) ; /* ie qs->len == 0 if qs->body == NULL */ +/*------------------------------------------------------------------------------ + * Extend given dst path by simply affixing the given src string. + */ +extern qpath +qpath_extend_str(qpath dst, const char* src) +{ + return qpath_extend_str_n(dst, src, (src != NULL) ? strlen(src) : 0) ; +} ; - off = qs->len ; /* where new stuff starts */ +/*------------------------------------------------------------------------------ + * Extend given dst path by simply affixing the given src string of the given + * length. + */ +extern qpath +qpath_extend_str_n(qpath dst, const char* src, ulen n) +{ + dst = qpath_make_if_null(dst) ; - if (ep != sp) - { - if (*(ep - 1) == '/') - --off ; /* step back to the '/' */ - else - { - /* Destination is not empty and does not end '/', so append one. - * - * Note that we ensure there is space for the path which are - * about to push, so at most one allocation required. - */ - qs_need(qs, (ep - sp) + 1 + len) ; - qs_append_n(qs, "/", 1) ; - } ; - } ; + qs_append_str_n(dst->path, src, n) ; - /* Now push path */ - qs_append_n(qs, path, len) ; + qpath_reduce(dst) ; - /* Reduce the new part of the result, and return - * - * Note that the 'off' points at the '/' which precedes the new stuff. - * So will spot "/./" where the new stuff starts "./". - */ - qpath_reduce(qp, off) ; + return dst ; +} ; - return qp ; +/*------------------------------------------------------------------------------ + * Prepend src path onto front of dst path. + * + * Like append, where the dst ends up being the dst appended to the src. + */ +extern qpath +qpath_prepend(qpath dst, const qpath src) +{ + if (src != NULL) + return qpath_prepend_str_n(dst, qs_char_nn(src->path), + qs_len_nn(src->path)) ; + else + return qpath_prepend_str_n(dst, NULL, 0) ; } ; /*------------------------------------------------------------------------------ - * Join two paths to create a new path. + * Prepend src qstring onto front of dst path. * - * Copies the destination path and then pushes the other path onto it. + * Like append, where the dst ends up being the dst appended to the src. */ extern qpath -qpath_join(qpath qp, qpath qp_a) +qpath_prepend_qs(qpath dst, const qstring src) { - qpath qp_n ; + return qpath_prepend_str_n(dst, qs_char(src), qs_len(src)) ; +} ; - qp_n = qpath_copy(qp) ; - return qpath_push(qp_n, qp_a) ; +/*------------------------------------------------------------------------------ + * Prepend src string onto front of dst path. + * + * Like append, where the dst ends up being the dst appended to the src. + */ +extern qpath +qpath_prepend_str(qpath dst, const char* src) +{ + return qpath_prepend_str_n(dst, src, (src != NULL) ? strlen(src) : 0) ; } ; /*------------------------------------------------------------------------------ - * Join path string to the given path to create a new path. + * Prepend src string of given length onto front of dst path. * - * Copies the destination path and then pushes the path string onto it. + * Like append, where the dst ends up being the dst appended to the src. */ extern qpath -qpath_join_str(qpath qp, const char* path) +qpath_prepend_str_n(qpath dst, const char* src, ulen n) +{ + char* p ; + ulen r ; + bool need_slash ; + + dst = qpath_make_if_null(dst) ; + + /* Trim the path to be prepended to: + * + * 1. discard from any leading '~' to the first '/' (or end). + * + * 2. then discard any leading '/' + * + * 3. then establish length of any part to be replaced. + */ + r = qs_len_nn(dst->path) ; + r -= qpath_trim_home(qs_char_nn(dst->path), r) ; + + /* Worry about whether need to add a '/' to the path before pushing */ + need_slash = (n > 0) && (*(src + n - 1) != '/') ; + + /* Make room for src and possible slash in qstring */ + qs_set_cp_nn(dst->path, 0) ; + qs_replace(dst->path, r, NULL, n + (need_slash ? 1 : 0)) ; + + /* Now copy in the src */ + p = qs_char_nn(dst->path) ; + + if (n > 0) + memmove(p, src, n) ; + + if (need_slash) + *(p + n) = '/' ; + + /* Reduce the new part of the result, and return */ + qpath_reduce(dst) ; + + return dst ; +} ; + +/*------------------------------------------------------------------------------ + * Make a qpath from the given string, completing it, if required, by + * prepending the given directory qpath. + */ +extern qpath +qpath_make(const char* src, const qpath dir) +{ + if (*src == '/') + return qpath_set(NULL, src) ; + + return qpath_append_str(qpath_dup(dir), src) ; +} ; + +/*------------------------------------------------------------------------------ + * If given dst path is not rooted (does not start with '/', prepend the + * given src path to it. Result is reduced. + */ +extern qpath +qpath_complete(qpath dst, const qpath src) +{ + if (src != NULL) + return qpath_prepend_str_n(dst, qs_char_nn(src->path), + qs_len_nn(src->path)) ; + else + return qpath_prepend_str_n(dst, NULL, 0) ; +} ; + +/*------------------------------------------------------------------------------ + * If given dst path is not rooted (does not start with '/', prepend the + * given src qstring to it. Result is reduced. + */ +extern qpath +qpath_complete_qs(qpath dst, const qstring src) +{ + return qpath_complete_str_n(dst, qs_char(src), qs_len(src)) ; +} ; + +/*------------------------------------------------------------------------------ + * If given dst path is not rooted (does not start with '/', prepend the + * given src string to it. Result is reduced. + */ +extern qpath +qpath_complete_str(qpath dst, const char* src) +{ + return qpath_prepend_str_n(dst, src, (src != NULL) ? strlen(src) : 0) ; +} ; + +/*------------------------------------------------------------------------------ + * If given dst path is not rooted (does not start with '/', prepend the + * given src string of given length to it. Result is reduced. + */ +extern qpath +qpath_complete_str_n(qpath dst, const char* src, ulen n) +{ + dst = qpath_make_if_null(dst) ; + + if ((qs_len_nn(dst->path) == 0) || (*(qs_char_nn(dst->path)) == '/')) + qpath_prepend_str_n(dst, src, n) ; + else + qpath_reduce(dst) ; + + return dst ; +} ; + +/*------------------------------------------------------------------------------ + * Trim leading '~' up to and including one or more '/'. + * + * Return remaining length after the trim. + */ +static ulen +qpath_trim_home(const char* p, ulen n) { - qpath qp_n ; + if ((n > 0) && (*p == '~')) + { + do /* Step past leadin '~' to first '/' */ + { + ++p ; + --n ; + } while ((n > 0) && (*p != '/')) ; + } ; + + while ((n > 0) && (*p == '/')) + { + ++p ; /* Step past leading '/' */ + --n ; + } ; - qp_n = qpath_copy(qp) ; - return qpath_push_str(qp_n, path) ; + return n ; } ; +/*============================================================================== + * + */ + +#if 0 + /*------------------------------------------------------------------------------ * Does the given path start and end '/' ? */ @@ -817,3 +1102,111 @@ qpath_is_atom(qpath qp) return false ; } ; +#endif + +/*============================================================================== + * + * + * + */ +/*------------------------------------------------------------------------------ + * Reduce multiple '/' to single '/' (except for exactly "//" at start). + * + * Reduce "/./" to "/". + */ +static void +qpath_reduce(qpath qp) +{ + char* sp ; + char* p ; + char* q ; + + sp = qs_make_string(qp->path) ; + p = sp ; + + /* Deal with special case of "//" at start. + * + * If find "//x", where x is anything other than '/', step past the first + * '/'. Could step past both "//", but that stops it seeing "//./zzz" + */ + if ((*p == '/') && (*(p + 1) == '/') && (*(p + 2) != '/')) + ++p ; + + /* Scan to see if there is anything that needs to be fixed. + * + * Looking for "//" and "/./". + */ + while (1) + { + if (*p == '\0') + return ; /* scanned to end */ + + if (*p++ != '/') /* scanning for '/' */ + continue ; + + if (*p == '/') + break ; /* second '/' */ + + if (*p != '.') + continue ; /* not "//" and not "/." */ + + if (*(p+1) == '/') + break ; /* found "/./" */ + } ; + + /* Rats... there is something to be fixed. + * + * *p is second '/' of "//" or '.' of "/./". + */ + q = p ; /* keep the first '/' */ + + while (*p != '\0') + { + ++p ; /* step past '.' or second '/' */ + + /* Step past any number of '/' and any number of "./". */ + while (*p != '\0') + { + while (*p == '/') /* eat any number of these */ + ++p ; + + if (*p != '.') /* done if not '.' */ + break ; + + if (*(p+1) != '/') /* done if not "./" */ + break ; + + p += 2 ; /* Step past "./" */ + } ; + + /* Here we have *p which is not '/' and not "./", so unless is '\0' + * there is at least one character to move across. + * + * Copying stuff, until get to '\0' or find "//" or "/./" + */ + while (*p != '\0') + { + *q++ = *p ; /* copy non-'\0' */ + + if (*p++ != '/') + continue ; /* keep going if wasn't '/' */ + + if (*p == '/') + break ; /* second '/' */ + + if (*p != '.') + continue ; /* not "//" and not "/." */ + + if (*(p+1) == '/') + break ; /* found "/./" */ + } ; + } ; + + /* Adjust the length and terminate */ + + qs_set_len_nn(qp->path, q - sp) ; /* set the new length (shorter !) */ +} ; + + + + |