public static function fullPath($path, $cwd = NULL, $allow_parent = FALSE) {
        //unix style full paths
        if(substr($path, 0, 1) == DIRECTORY_SEPARATOR) {
            return $path;
        }

        //windows style full paths
        if(substr($path, 1, 2) == ":\\") {
            return $path;
        }

        //throw exception if there are parent paths
        if(!$allow_parent) {
            $parts = explode(DIRECTORY_SEPARATOR, $path);
            if(array_search("..", $parts) !== FALSE) {
                throw new Exception("Parent directory in path '$path' detected. This is currently not allowed.");
            }
        }

        //get paths (with unresolved parent paths)
        $path = rtrim($cwd ?: getcwd(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$path;

        //adjust windows paths for further processing
        if(substr($path, 1, 2) == ":\\") {
            $prefix = substr($path, 0, 3);
            $path = substr($path, 3);
        }
        else {
            $prefix = "";
        }

        //get parts
        $parts = explode(DIRECTORY_SEPARATOR, $path);

        //remove parts in front of .. parts
        $result = [];

        while($parts) {
            $part = array_shift($parts);

            if($part == "..") {
                if(count($result) <= 1) {
                    throw new Exception("'$path' can not exist.");
                }

                array_pop($result);
            }
            else {
                array_push($result, $part);
            }
        }

        //return new assembled path
        return $prefix.implode(DIRECTORY_SEPARATOR, $result);
    }