Source

gophrier / path.c

/*  Copyright 2010-2011 Guillaume Duhamel

    This file is part of Gophrier.

    Gophrier is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Gophrier is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Gophrier; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

#include "path.h"
#include "dbstring.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

static void path_sync(Path * path);

Path * path_new(const char * inpath, const char * context)
{
    Path * path;
    char * copy, * current, * next;

    path = (Path *) malloc(sizeof(Path));
    path->components = NULL;
    path->sync = 0;
    path->len = 0;
    path->path = NULL;

    if (NULL != context)
    {
        copy = malloc(strlen(context) + strlen(inpath) + 2);
        sprintf(copy, "%s/%s", context, inpath);
    }
    else if ((strlen(inpath) > 0) && (inpath[0] == '/'))
        copy = strdup(inpath);
    else
    {
        char * wd = get_current_dir_name();
        copy = malloc(strlen(wd) + strlen(inpath) + 2);
        sprintf(copy, "%s/%s", wd, inpath);
        free(wd);
    }

    current = copy;
    while(current)
    {
        next = strchr(current, '/');
        if (next)
        {
            *next = 0;
            path_push(path, current);
            current = next + 1;
        }
        else
        {
            path_push(path, current);
            current = NULL;
        }
    }

    path->sync = 1;
    path_sync(path);

    free(copy);

    return path;
}

void path_free(Path * path)
{
    list_free(path->components);
    free(path->path);
    free(path);
}

void path_push(Path * path, const char * component)
{
    unsigned int len;

    if (NULL == component) return;
    if ('\0' == *component) return;

    len = strlen(component);

    if (('.' == *component) && (1 == len)) return;

    if ((2 == len) && ('.' == component[0]) && ('.' == component[1]))
    {
        char * removed;
        path->components = list_pop(path->components, (void **) &removed);
        if (NULL != removed)
        {
            path->len -= 1 + strlen(removed);
            free(removed);
        }
    }
    else
    {
        path->components = list_push(path->components, strdup(component));
        path->len += 1 + len;
    }
    if (path->sync)
        path_sync(path);
}

void path_sync(Path * path)
{
    List * list;
    char * pos;
    struct stat pathinfo;

    path->mode = 0;
    path->valid = 1;

    path->path = realloc(path->path, 1 + path->len);
    if (! path->components)
    {
        path->path[0] = '\0';
        return;
    }
    pos = path->path;
    list = path->components;
    while(list)
    {
        pos += sprintf(pos, "/%s", (char *) list->current);
        path->valid = (0 == lstat(path->path, &pathinfo));
        if (path->valid)
        {
            if (S_ISLNK(pathinfo.st_mode))
                path->mode |= S_IFLNK;
        }
        list = list->next;
    }
    if (path->valid)
    {
        if (S_ISLNK(pathinfo.st_mode))
            stat(path->path, &pathinfo);
        if (S_ISDIR(pathinfo.st_mode))
            path->mode |= S_IFDIR;

        if (S_ISREG(pathinfo.st_mode) && (! path_is_link(path)))
            path->mode |= S_IFREG;
    }
    return;
}

int path_is_link(Path * path) {
    return S_IFLNK == (path->mode & S_IFLNK);
}

int path_is_dir(Path * path) {
    return S_IFDIR == (path->mode & S_IFDIR);
}

int path_is_executable(Path * path) {
    return S_IXOTH == (path->mode & S_IXOTH);
}

int path_is_regular(Path * path) {
    return S_IFREG == (path->mode & S_IFREG);
}