|  | @@ -24,115 +24,143 @@
 | 
	
		
			
				|  |  |  #include <api.h>
 | 
	
		
			
				|  |  |  #include <pal_error.h>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -int get_norm_path (const char * path, char * buf, int offset, int size)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -    int head = offset;
 | 
	
		
			
				|  |  | -    char c, c1;
 | 
	
		
			
				|  |  | -    const char * p = path;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    while (head) { /* find the real head, not interrupted by dot-dot */
 | 
	
		
			
				|  |  | -        if (head > 1 && buf[head - 1] == '.' && buf[head - 2] == '.')
 | 
	
		
			
				|  |  | -            break;
 | 
	
		
			
				|  |  | -        head--;
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Finds next '/' in `path`.
 | 
	
		
			
				|  |  | + * Returns a pointer to it or to the nullbyte ending the string if no '/' has been found.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static inline const char* find_next_slash(const char* path) {
 | 
	
		
			
				|  |  | +    while (*path && *path != '/') {
 | 
	
		
			
				|  |  | +        path++;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return path;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Finds previous '/' in `path` (starting from `size` - 1) and returns offset to it.
 | 
	
		
			
				|  |  | + * If the last character is '/', then it is skipped (as a token can end with '/').
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static inline size_t find_prev_slash_offset(const char* path, size_t size) {
 | 
	
		
			
				|  |  | +    if (size && path[size - 1] == '/') {
 | 
	
		
			
				|  |  | +        size--;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    while (size && path[size - 1] != '/') {
 | 
	
		
			
				|  |  | +        size--;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return size;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Before calling this function *size_ptr should hold the size of buf.
 | 
	
		
			
				|  |  | + * After returning it holds number of bytes actually written to it
 | 
	
		
			
				|  |  | + * (excluding the ending '\0').
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +int get_norm_path(const char* path, char* buf, size_t* size_ptr) {
 | 
	
		
			
				|  |  | +    if (!path || !buf || !size_ptr) {
 | 
	
		
			
				|  |  | +        return -PAL_ERROR_INVAL;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    for (c = '/' ; c ; c = c1, p++) {
 | 
	
		
			
				|  |  | -        c1 = *p;
 | 
	
		
			
				|  |  | -        if (c == '/') {     /* find a slash, or the beginning of the path */
 | 
	
		
			
				|  |  | -            if (c1 == 0)    /* no more path */
 | 
	
		
			
				|  |  | -                break;
 | 
	
		
			
				|  |  | -            if (c1 == '/')  /* consequential slashes */
 | 
	
		
			
				|  |  | -                continue;
 | 
	
		
			
				|  |  | -            if (c1 == '.') {    /* find a dot, can be dot-dot or a file */
 | 
	
		
			
				|  |  | -                c1 = *(++p);
 | 
	
		
			
				|  |  | -                if (c1 == 0)    /* no more path */
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                if (c1 == '/')  /* a dot, skip it */
 | 
	
		
			
				|  |  | -                    continue;
 | 
	
		
			
				|  |  | -                if (c1 == '.') {    /* must be dot-dot */
 | 
	
		
			
				|  |  | -                    c1 = *(++p);
 | 
	
		
			
				|  |  | -                    if (c1 != 0 && c1 != '/') { /* Paths can start with a dot
 | 
	
		
			
				|  |  | -                                                 * dot: ..xyz is ok */
 | 
	
		
			
				|  |  | -                        if (offset >= size - 2)
 | 
	
		
			
				|  |  | -                            return -PAL_ERROR_TOOLONG;
 | 
	
		
			
				|  |  | -                        buf[offset++] = '.';
 | 
	
		
			
				|  |  | -                        buf[offset++] = '.';
 | 
	
		
			
				|  |  | -                        continue;
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    if (offset > head) {    /* remove the last token */
 | 
	
		
			
				|  |  | -                        while (offset > head && buf[--offset] != '/');
 | 
	
		
			
				|  |  | -                    } else {
 | 
	
		
			
				|  |  | -                        if (offset) {   /* add a slash */
 | 
	
		
			
				|  |  | -                            if (offset >= size - 1)
 | 
	
		
			
				|  |  | -                                return -PAL_ERROR_TOOLONG;
 | 
	
		
			
				|  |  | -                            buf[offset++] = '/';
 | 
	
		
			
				|  |  | -                        }               /* add a dot-dot */
 | 
	
		
			
				|  |  | -                        if (offset >= size - 2)
 | 
	
		
			
				|  |  | -                            return -PAL_ERROR_TOOLONG;
 | 
	
		
			
				|  |  | -                        buf[offset++] = '.';
 | 
	
		
			
				|  |  | -                        buf[offset++] = '.';
 | 
	
		
			
				|  |  | -                        head = offset;
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                } else { /* it's a file */
 | 
	
		
			
				|  |  | -                    if (offset) {   /* add a slash */
 | 
	
		
			
				|  |  | -                        if (offset >= size - 1)
 | 
	
		
			
				|  |  | -                            return -PAL_ERROR_TOOLONG;
 | 
	
		
			
				|  |  | -                        buf[offset++] = '/';
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    if (offset >= size - 1)
 | 
	
		
			
				|  |  | -                        return -PAL_ERROR_TOOLONG;
 | 
	
		
			
				|  |  | -                    buf[offset++] = '.';
 | 
	
		
			
				|  |  | +    size_t size = *size_ptr;
 | 
	
		
			
				|  |  | +    if (!size) {
 | 
	
		
			
				|  |  | +        return -PAL_ERROR_ZEROSIZE;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    /* reserve 1 byte for ending '\0' */
 | 
	
		
			
				|  |  | +    size--;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    size_t offset = 0,
 | 
	
		
			
				|  |  | +           ret_size = 0; /* accounts for undiscardable bytes written to `buf`
 | 
	
		
			
				|  |  | +                          * i.e. `buf - ret_size` points to original `buf` */
 | 
	
		
			
				|  |  | +    unsigned char need_slash = 0; // is '/' needed before next token
 | 
	
		
			
				|  |  | +    bool is_absolute_path = *path == '/';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* handle an absolute path */
 | 
	
		
			
				|  |  | +    if (is_absolute_path) {
 | 
	
		
			
				|  |  | +        if (size < 1) {
 | 
	
		
			
				|  |  | +            return -PAL_ERROR_TOOLONG;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        *buf++ = '/';
 | 
	
		
			
				|  |  | +        size--;
 | 
	
		
			
				|  |  | +        ret_size++;
 | 
	
		
			
				|  |  | +        path++;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    while (1) {
 | 
	
		
			
				|  |  | +        /* handle next token */
 | 
	
		
			
				|  |  | +        const char* end = find_next_slash(path);
 | 
	
		
			
				|  |  | +        if (end - path == 2 && path[0] == '.' && path[1] == '.') {
 | 
	
		
			
				|  |  | +            /* ".." */
 | 
	
		
			
				|  |  | +            if (offset) {
 | 
	
		
			
				|  |  | +                /* eat up previously written token */
 | 
	
		
			
				|  |  | +                offset = find_prev_slash_offset(buf, offset);
 | 
	
		
			
				|  |  | +                need_slash = 0;
 | 
	
		
			
				|  |  | +            } else if (!is_absolute_path) {
 | 
	
		
			
				|  |  | +                /* append undiscardable ".." since there is no previous token
 | 
	
		
			
				|  |  | +                 * but only if the path is not absolute */
 | 
	
		
			
				|  |  | +                if (need_slash + 2u > size) {
 | 
	
		
			
				|  |  | +                    return -PAL_ERROR_TOOLONG;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                if (need_slash) {
 | 
	
		
			
				|  |  | +                    *buf++ = '/';
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -                continue;
 | 
	
		
			
				|  |  | +                *buf++ = '.';
 | 
	
		
			
				|  |  | +                *buf++ = '.';
 | 
	
		
			
				|  |  | +                size -= need_slash + 2u;
 | 
	
		
			
				|  |  | +                ret_size += need_slash + 2u;
 | 
	
		
			
				|  |  | +                need_slash = 1;
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +                /* remaining case: offset == 0, path is absolute and ".." was just seen,
 | 
	
		
			
				|  |  | +                 * i.e. "/..", which is collapsed to "/", hence nothing needs to be done */
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        if (offset || c != '/' || *path == '/') {
 | 
	
		
			
				|  |  | -            if (offset >= size - 1)
 | 
	
		
			
				|  |  | +        } else if ((end == path) || (end - path == 1 && path[0] == '.')) {
 | 
	
		
			
				|  |  | +            /* ignore "//" and "." */
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            size_t len = (size_t)(end - path);
 | 
	
		
			
				|  |  | +            if (need_slash + len > size - offset) {
 | 
	
		
			
				|  |  |                  return -PAL_ERROR_TOOLONG;
 | 
	
		
			
				|  |  | -            buf[offset++] = c;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            if (need_slash) {
 | 
	
		
			
				|  |  | +                buf[offset++] = '/';
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            memcpy(buf + offset, path, len);
 | 
	
		
			
				|  |  | +            offset += len;
 | 
	
		
			
				|  |  | +            need_slash = 1;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +        if (!*end) {
 | 
	
		
			
				|  |  | +            break;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        path = end + 1;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    buf[offset] = 0;
 | 
	
		
			
				|  |  | -    return offset;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +    buf[offset] = '\0';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -int get_base_name (const char * path, char * buf, int size)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -    const char * p = path;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    for (; *p ; p++) {
 | 
	
		
			
				|  |  | -        if (*p == '/')
 | 
	
		
			
				|  |  | -            continue;
 | 
	
		
			
				|  |  | -        if (*p == '.') {
 | 
	
		
			
				|  |  | -            if (*(p + 1) == '/' || !*(p + 1)) {
 | 
	
		
			
				|  |  | -                p++;
 | 
	
		
			
				|  |  | -                continue;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            if (*(p + 1) == '.') {
 | 
	
		
			
				|  |  | -                if (*(p + 2) == '/' || !*(p + 2)) {
 | 
	
		
			
				|  |  | -                    p += 2;
 | 
	
		
			
				|  |  | -                    continue;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +    *size_ptr = ret_size + offset;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        const char * e = p + 1;
 | 
	
		
			
				|  |  | -        for (; *e && *e != '/' ; e++);
 | 
	
		
			
				|  |  | -        if (*e) {
 | 
	
		
			
				|  |  | -            p = e - 1;
 | 
	
		
			
				|  |  | -            continue;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +    return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if (e - p > size - 1)
 | 
	
		
			
				|  |  | -            return -PAL_ERROR_TOOLONG;
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Before calling this function *size should hold the size of buf.
 | 
	
		
			
				|  |  | + * After returning it holds number of bytes actually written to it
 | 
	
		
			
				|  |  | + * (excluding the trailing '\0').
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +int get_base_name(const char* path, char* buf, size_t* size) {
 | 
	
		
			
				|  |  | +    if (!path || !buf || !size) {
 | 
	
		
			
				|  |  | +        return -PAL_ERROR_INVAL;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const char* end;
 | 
	
		
			
				|  |  | +    while (*(end = find_next_slash(path))) {
 | 
	
		
			
				|  |  | +        path = end + 1;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        int offset = 0;
 | 
	
		
			
				|  |  | -        for (; p < e ; p++, offset++)
 | 
	
		
			
				|  |  | -            buf[offset] = *p;
 | 
	
		
			
				|  |  | -        buf[offset] = 0;
 | 
	
		
			
				|  |  | -        return offset;
 | 
	
		
			
				|  |  | +    size_t result = (size_t)(end - path);
 | 
	
		
			
				|  |  | +    if (result + 1 > *size) {
 | 
	
		
			
				|  |  | +        return -PAL_ERROR_TOOLONG;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    memcpy(buf, path, result);
 | 
	
		
			
				|  |  | +    buf[result] = '\0';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    *size = result;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      return 0;
 | 
	
		
			
				|  |  |  }
 |