Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>

/*
 * In place, rewrite name to compress multiple /, eliminate ., and process ..
 */
#define SEP(x)  ((x)=='/' || (x) == 0)
char*
cleanname(char *name)
{
        char *s;        /* source of copy */
        char *d;        /* destination of copy */
        char *d0;       /* start of path afer the root name */
        Rune r;
        int rooted;

        if(name[0] == 0)
                return strcpy(name, ".");
        rooted = 0;
        d0 = name;
        if(d0[0] == '#'){
                if(d0[1] == 0)
                        return d0;
                d0  += 1 + chartorune(&r, d0+1); /* ignore slash: #/ */
                while(!SEP(*d0))
                        d0 += chartorune(&r, d0);
                if(d0 == 0)
                        return name;
                d0++;   /* keep / after #<name> */
                rooted = 1;
        }else if(d0[0] == '/'){
                rooted = 1;
                d0++;
        }

        s = d0;
        if(rooted){
                /* skip extra '/' at root name */
                for(; *s == '/'; s++)
                        ;
        }
        /* remove dup slashes */
        for(d = d0; *s != 0; s++){
                *d++ = *s;
                if(*s == '/')
                        while(s[1] == '/')
                                s++;
        }
        *d = 0;

        d = d0;
        s = d0;
        while(*s != 0){
                if(s[0] == '.' && SEP(s[1])){
                        if(s[1] == 0)
                                break;
                        s+= 2;
                        continue;
                }
                if(s[0] == '.' && s[1] == '.' && SEP(s[2])){
                        if(d == d0){
                                if(rooted){
                                        /* /../x -> /x */
                                        if(s[2] == 0)
                                                break;
                                        s += 3;
                                        continue;
                                }else{
                                        /* ../x -> ../x; and never collect ../ */
                                        d0 += 3;
                                }
                        }
                        if(d > d0){
                                /* a/../x -> x */
                                assert(d-2 >= d0 && d[-1] == '/');
                                for(d -= 2; d > d0 && d[-1] != '/'; d--)
                                                ;
                                if(s[2] == 0)
                                        break;
                                s += 3;
                                continue;
                        }
                }
                while(!SEP(*s))
                        *d++ = *s++;
                if(*s == 0)
                        break;
                
                *d++ = *s++;
        }
        *d = 0;
        if(d-1 > name && d[-1] == '/')  /* thanks to #/ */
                *--d = 0;
        if(name[0] == 0)
                strcpy(name, ".");
        return name;
}