Пример #1
0
static int
read_mtree_spec(FILE *fp)
{
	char pathspec[PATH_MAX];
	char *cp;
	int error;

	error = read_word(fp, pathspec, sizeof(pathspec));
	if (error)
		goto out;

	cp = strchr(pathspec, '/');
	if (cp != NULL) {
		/* Absolute pathname */
		mtree_current = mtree_root;

		do {
			*cp++ = '\0';

			/* Disallow '..' as a component. */
			if (IS_DOTDOT(pathspec)) {
				mtree_error("absolute path cannot contain "
				    ".. component");
				goto out;
			}

			/* Ignore multiple adjacent slashes and '.'. */
			if (pathspec[0] != '\0' && !IS_DOT(pathspec))
				error = read_mtree_spec1(fp, false, pathspec);
			memmove(pathspec, cp, strlen(cp) + 1);
			cp = strchr(pathspec, '/');
		} while (!error && cp != NULL);

		/* Disallow '.' and '..' as the last component. */
		if (!error && (IS_DOT(pathspec) || IS_DOTDOT(pathspec))) {
			mtree_error("absolute path cannot contain . or .. "
			    "components");
			goto out;
		}
	}

	/* Ignore absolute specfications that end with a slash. */
	if (!error && pathspec[0] != '\0')
		error = read_mtree_spec1(fp, true, pathspec);

 out:
	skip_to(fp, "\n");
	(void)getc(fp);
	return (error);
}
/*
 * Create path from the name and parent of an entry.
 */
static char *
create_v1_path(struct mtree_entry *entry)
{
	char	*path;
	char	*prefix = "";
	size_t	 len;

	len = strlen(entry->name);
	if (entry->parent == NULL) {
		if (!IS_DOT(entry->name))
			prefix = "./";
	} else
		len += strlen(entry->parent->path) + 1;

	len += strlen(prefix);
	path = malloc(len + 1);
	if (path != NULL)
		snprintf(path, len + 1, "%s%s%s%s",
		    prefix,
		    entry->parent ? entry->parent->path : "",
		    entry->parent ? "/" : "",
		    entry->name);
	return (path);
}
Пример #3
0
static int
read_mtree_spec1(FILE *fp, bool def, const char *name)
{
	fsnode *last, *node, *parent;
	u_int type;
	int error;

	assert(name[0] != '\0');

	/*
	 * Treat '..' specially, because it only changes our current
	 * directory. We don't create a node for it. We simply ignore
	 * any keywords that may appear on the line as well.
	 * Going up a directory is a little non-obvious. A directory
	 * node has a corresponding '.' child. The parent of '.' is
	 * not the '.' node of the parent directory, but the directory
	 * node within the parent to which the child relates. However,
	 * going up a directory means we need to find the '.' node to
	 * which the directoy node is linked.  This we can do via the
	 * first * pointer, because '.' is always the first entry in a
	 * directory.
	 */
	if (IS_DOTDOT(name)) {
		/* This deals with NULL pointers as well. */
		if (mtree_current == mtree_root) {
			mtree_warning("ignoring .. in root directory");
			return (0);
		}

		node = mtree_current;

		assert(node != NULL);
		assert(IS_DOT(node->name));
		assert(node->first == node);

		/* Get the corresponding directory node in the parent. */
		node = mtree_current->parent;

		assert(node != NULL);
		assert(!IS_DOT(node->name));

		node = node->first;

		assert(node != NULL);
		assert(IS_DOT(node->name));
		assert(node->first == node);

		mtree_current = node;
		return (0);
	}

	/*
	 * If we don't have a current directory and the first specification
	 * (either implicit or defined) is not '.', then we need to create
	 * a '.' node first (using a recursive call).
	 */
	if (!IS_DOT(name) && mtree_current == NULL) {
		error = read_mtree_spec1(fp, false, ".");
		if (error)
			return (error);
	}

	/*
	 * Lookup the name in the current directory (if we have a current
	 * directory) to make sure we do not create multiple nodes for the
	 * same component. For non-definitions, if we find a node with the
	 * same name, simply change the current directory. For definitions
	 * more happens.
	 */
	last = NULL;
	node = mtree_current;
	while (node != NULL) {
		assert(node->first == mtree_current);

		if (strcmp(name, node->name) == 0) {
			if (def == true) {
				if (!dupsok)
					mtree_error(
					    "duplicate definition of %s",
					    name);
				else
					mtree_warning(
					    "duplicate definition of %s",
					    name);
				return (0);
			}

			if (node->type != S_IFDIR) {
				mtree_error("%s is not a directory", name);
				return (0);
			}

			assert(!IS_DOT(name));

			node = node->child;

			assert(node != NULL);
			assert(IS_DOT(node->name));

			mtree_current = node;
			return (0);
		}

		last = node;
		node = last->next;
	}

	parent = (mtree_current != NULL) ? mtree_current->parent : NULL;
	type = (def == false || IS_DOT(name)) ? S_IFDIR : 0;
	node = create_node(name, type, parent, &mtree_global);
	if (node == NULL)
		return (ENOMEM);

	if (def == true) {
		error = read_mtree_keywords(fp, node);
		if (error) {
			destroy_node(node);
			return (error);
		}
	}

	node->first = (mtree_current != NULL) ? mtree_current : node;

	if (last != NULL)
		last->next = node;

	if (node->type != S_IFDIR)
		return (0);

	if (!IS_DOT(node->name)) {
		parent = node;
		node = create_node(".", S_IFDIR, parent, parent);
		if (node == NULL) {
			last->next = NULL;
			destroy_node(parent);
			return (ENOMEM);
		}
		parent->child = node;
		node->first = node;
	}

	assert(node != NULL);
	assert(IS_DOT(node->name));
	assert(node->first == node);

	mtree_current = node;
	if (mtree_root == NULL)
		mtree_root = node;

	return (0);
}
Пример #4
0
/* dnormalize():
 *	The path will be normalized if it
 *	1) is "..",
 *	2) or starts with "../",
 *	3) or ends with "/..",
 *	4) or contains the string "/../",
 *	then it will be normalized, unless those strings are quoted. 
 *	Otherwise, a copy is made and sent back.
 */
Char   *
dnormalize(const Char *cp, int expnd)
{

/* return true if dp is of the form "../xxx" or "/../xxx" */
#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))

#ifdef S_IFLNK
    if (expnd) {
	struct Strbuf buf = Strbuf_INIT;
 	int     dotdot = 0;
	Char   *dp, *cwd;
	const Char *start = cp;
# ifdef HAVE_SLASHSLASH
	int slashslash;
# endif /* HAVE_SLASHSLASH */

	/*
	 * count the number of "../xxx" or "xxx/../xxx" in the path
	 */
	for ( ; *cp && *(cp + 1); cp++)
	    if (IS_DOTDOT(start, cp))
	        dotdot++;

	/*
	 * if none, we are done.
	 */
        if (dotdot == 0)
	    return (Strsave(start));
	
# ifdef notdef
	struct stat sb;
	/*
	 * We disable this test because:
	 * cd /tmp; mkdir dir1 dir2; cd dir2; ln -s /tmp/dir1; cd dir1;
	 * echo ../../dir1 does not expand. We had enabled this before
	 * because it was bothering people with expansions in compilation
	 * lines like -I../../foo. Maybe we need some kind of finer grain
	 * control?
	 *
	 * If the path doesn't exist, we are done too.
	 */
	if (lstat(short2str(start), &sb) != 0 && errno == ENOENT)
	    return (Strsave(start));
# endif

	cwd = xmalloc((Strlen(dcwd->di_name) + 3) * sizeof(Char));
	(void) Strcpy(cwd, dcwd->di_name);

	/*
	 * If the path starts with a slash, we are not relative to
	 * the current working directory.
	 */
	if (ABSOLUTEP(start))
	    *cwd = '\0';
# ifdef HAVE_SLASHSLASH
	slashslash = cwd[0] == '/' && cwd[1] == '/';
# endif /* HAVE_SLASHSLASH */

	/*
	 * Ignore . and count ..'s
	 */
	cp = start;
	do {
	    dotdot = 0;
	    buf.len = 0;
	    while (*cp) 
	        if (IS_DOT(start, cp)) {
	            if (*++cp)
	                cp++;
	        }
	        else if (IS_DOTDOT(start, cp)) {
		    if (buf.len != 0)
		        break; /* finish analyzing .././../xxx/[..] */
		    dotdot++;
		    cp += 2;
		    if (*cp)
		        cp++;
	        }
	        else 
		    Strbuf_append1(&buf, *cp++);

	    Strbuf_terminate(&buf);
	    while (dotdot > 0) 
	        if ((dp = Strrchr(cwd, '/')) != NULL) {
# ifdef HAVE_SLASHSLASH
		    if (dp == &cwd[1]) 
		        slashslash = 1;
# endif /* HAVE_SLASHSLASH */
		        *dp = '\0';
		        dotdot--;
	        }
	        else
		    break;

	    if (!*cwd) {	/* too many ..'s, starts with "/" */
	        cwd[0] = '/';
# ifdef HAVE_SLASHSLASH
		/*
		 * Only append another slash, if already the former cwd
		 * was in a double-slash path.
		 */
		cwd[1] = slashslash ? '/' : '\0';
		cwd[2] = '\0';
# else /* !HAVE_SLASHSLASH */
		cwd[1] = '\0';
# endif /* HAVE_SLASHSLASH */
	    }
# ifdef HAVE_SLASHSLASH
	    else if (slashslash && cwd[1] == '\0') {
		cwd[1] = '/';
		cwd[2] = '\0';
	    }
# endif /* HAVE_SLASHSLASH */

	    if (buf.len != 0) {
		size_t i;

		i = Strlen(cwd);
		if (TRM(cwd[i - 1]) != '/') {
		    cwd[i++] = '/';
		    cwd[i] = '\0';
		}
	        dp = Strspl(cwd, TRM(buf.s[0]) == '/' ? &buf.s[1] : buf.s);
	        xfree(cwd);
	        cwd = dp;
		i = Strlen(cwd) - 1;
	        if (TRM(cwd[i]) == '/')
		    cwd[i] = '\0';
	    }
	    /* Reduction of ".." following the stuff we collected in buf
	     * only makes sense if the directory item in buf really exists.
	     * Avoid reduction of "-I../.." (typical compiler call) to ""
	     * or "/usr/nonexistant/../bin" to "/usr/bin":
	     */
	    if (cwd[0]) {
	        struct stat exists;
		if (0 != stat(short2str(cwd), &exists)) {
		    xfree(buf.s);
		    xfree(cwd);
		    return Strsave(start);
		}
	    }
	} while (*cp != '\0');
	xfree(buf.s);
	return cwd;
    }
#endif /* S_IFLNK */
    return Strsave(cp);
}
Пример #5
0
/*
 * walkdir()
 * Toto je slavny `algoritmus pruchodu adresarouvou strukturou`
 * Funkce je napsana rekurzivne, nebot adresare mohou ukryvat podadresare.
 * Rekurzivni implementace bude prehledna a spolehliva.
 * Argumenty:
 *	topdir	- nazev adresare odkud se fce bude zanorovat dale
 *	dea	- dir entry action struktura popisuje akci, kterou
 *		je treba vykonat nad kazdou nalezenou polozkou
 * Vraci:
 *	0 O.K., -1 Failure
 */
static int 
walkdir(const char *topdir)
{
	DIR * d;
	struct dirent *de;
	int	rv;
	struct stat st;
	unsigned int path_len;
	char *path;
	int	i;
	size_t	child_data;
	
	/*
	 * vsimnete si pouziti funkce pathconf(), ktera nam zjisti maximalni
	 * moznou velikost cesty na danem file systemu.  je treba si uvedomit,
	 * ze prave prochazeny adresar `topdir`, muze byt `mountpoint` na nemz
	 * sedi uplne jiny filesystem s jinou delkou cesty.
	 *
	 * nasledna dynamicka alokace bufferu pro cestu je spolehlivejsi
	 * alternativa k definici path jako `char path[20]`. Je to konec
	 * dohadu jestli je napr. 4096 dost a nebo malo pro ulozeni cesty.
	 * spravne je:
	 *	strlen(topdir) + pathconf(topdir, _PC_PATH_MAX).
	 */
	path_len = pathconf(topdir, _PC_PATH_MAX) + strlen(topdir);
	path = (char *)malloc(path_len);

	/*
	 * Obvykla triada, kdyz dojde pamet, nemuzeme udelat nic jineho,
	 * nez si postezovat a skoncit.
	 */
	if (path == NULL) {
		(void) fprintf(stderr,
		    "malloc(%u) for path buffer failed (%s)\n",
		    path_len, strerror(errno));
		return (-1);
	}

	/*
	 * jsme optimiste, predpokladame uspech
	 */
	rv = 0;

	if ((d = opendir(topdir)) != NULL) {

		/*
		 * zacneme prochazet adresar, polozku po polozce.
		 */
		while ((de = readdir(d)) != NULL) {

			/*
			 * pro stat() si musime sestavit absolutni cestu jako:
			 *	topdir + '/' + de->d_name
			 */
			(void) snprintf(path, path_len, "%s/%s",
			    topdir, de->d_name);
			printf("%s\n", path);
			if (stat(path, &st) == 0) {
				/*
				 * pro podadresare vytvorime novy proces.
				 */
				if (S_ISDIR(st.st_mode) &&
				    !IS_DOT(de->d_name)) {
					switch (fork()) {
					case 0:
						fifo_parent = open(fifo_path, O_WRONLY);
						proc_count = 0;
						length = 0;
						if (fifo_fd != -1) {
							rv = run_walkdir(path);
						} else {
							rv = 1;
						}
						exit(rv);
						break;
					case -1:
						exit(1);
						break;
					default:
						proc_count++;
					}
				} else if (S_ISREG(st.st_mode)) {
					length += st.st_size;
				}
			} 
		}

		/*
		 * Jakmile projdeme adresar, zavolame closedir(), abychom
		 * nevykradali prilis mnoho zdroju. Jinak by nas OS mohl rychle
		 * kleponout pres prsty u rozsahlejsich adresaru.
		 */
		(void)closedir(d);

		/* obtain results from child processes */
		for (i = 0; i < proc_count; i++) {
			if (read(fifo_fd, &child_data, sizeof (size_t)) ==
			    (ssize_t)sizeof (size_t)) {
				length += child_data;
			} else {
				perror("read failed");
			}
		}

		for (i = 0; i < proc_count; i++) {
			(void) wait(NULL);
		}

	} else {
		rv = -1;
	}

	/*
	 * Je potreba vratit pamet, kterou jsme si `pujcili` na zacatku funkce.
	 */
	free(path);

	/*
	 * posli rodici velikost, kterou jsme napocetli.
	 */
	if (fifo_parent != -1) {
		(void) write(fifo_parent, &length, sizeof (size_t));
		(void) close(fifo_parent);
	}

	return (rv);
}
Пример #6
0
/************************************************************************
**  get_real : gathers the real part/exponent of a real number.
**              Input  : ptr to the null terminator of the whole part
**                               pointer to receive value.
**              Output : L_CFLOAT
**
**  ASSUMES whole part is either at Exp_ptr or Reuse_W.
************************************************************************/
token_t    get_real(REG PWCHAR p)
{
    REG int             c;
    token_t     tok;

    c = get_non_eof();
    if(Cross_compile && (Tiny_lexer_nesting == 0)) {
        strcpy (Msg_Text, GET_MSG (4012));
        warning(4012);  /* float constant in cross compilation */
        Cross_compile = FALSE;  /*  only one msg per file */
    }
    /*
**  if the next char is a digit, then we've been called after
**  finding a '.'. if this is true, then
**  we want to find the fractional part of the number.
**  if it's a '.', then we've been called after finding
**  a whole part, and we want the fraction.
*/
    if( LXC_IS_DIGIT((WCHAR)c) || IS_DOT(c) ) {
        do {
            *p++ = (WCHAR)c;
            c = (int)get_non_eof();
        } while( LXC_IS_DIGIT((WCHAR)c) );
    }
    if( IS_E((WCHAR)c) ) {              /*  now have found the exponent  */
        *p++ = (WCHAR)c;                /*  save the 'e'  */
        c = (WCHAR)get_non_eof();       /*  skip it  */
        if( IS_SIGN(c) ) {              /*  optional sign  */
            *p++ = (WCHAR)c;            /*  save the sign  */
            c = (int)get_non_eof();
        }
        if( ! LXC_IS_DIGIT((WCHAR)c)) {
            if( ! Rflag ) {
                if(Tiny_lexer_nesting == 0) {
                    Msg_Temp = GET_MSG (2021);
                    SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, c);
                    error(2021); /* missing or malformed exponent */
                }
                *p++ = L'0';
            }
        }
        else {
            do {                        /* gather the exponent */
                *p++ = (WCHAR)c;
                c = (int)get_non_eof();
            } while( LXC_IS_DIGIT((WCHAR)c) );
        }
    }
    if( IS_F((WCHAR)c) ) {
        tok = L_CFLOAT;
        if( Prep ) {
            *p++ = (WCHAR)c;
        }
    }
    else if( IS_EL((WCHAR)c) ) {
        tok = L_CLDOUBLE;
        if( Prep ) {
            *p++ = (WCHAR)c;
        }
    }
    else {
        UNGETCH();
        tok = L_CDOUBLE;
    }
    *p = L'\0';
    if( Tiny_lexer_nesting > 0 ) {
        Exp_ptr = p;
        return(L_NOTOKEN);
    }
    else if( Prep ) {
        myfwrite( Reuse_W, (size_t)(p - Reuse_W) * sizeof(WCHAR), 1, OUTPUTFILE);
        return(L_NOTOKEN);
    }
    /*
        ** reals aren't used during preprocessing
        */
    return(tok);
}
Пример #7
0
/************************************************************************
 * GETNUM - Get a number from the input stream.
 *
 * ARGUMENTS
 *      radix - the radix of the number to be accumulated.  Can only be 8, 10,
 *                      or 16
 *      pval - a pointer to a VALUE union to be filled in with the value
 *
 * RETURNS - type of the token (L_CINTEGER or L_CFLOAT)
 *
 * SIDE EFFECTS -
 *      does push back on the input stream.
 *      writes into pval by reference
 *  uses buffer Reuse_W
 *
 * DESCRIPTION -
 *      Accumulate the number according to the rules for each radix.
 *      Set up the format string according to the radix (or distinguish
 *      integer from float if radix is 10) and convert to binary.
 *
 * AUTHOR - Ralph Ryan, Sept. 8, 1982
 *
 * MODIFICATIONS - none
 *
 ************************************************************************/
token_t getnum(REG      WCHAR           c)
{
    REG WCHAR   *p;
    WCHAR       *start;
    int         radix;
    token_t     tok;
    value_t     value;

    tok = L_CINTEGER;
    start = (Tiny_lexer_nesting ? Exp_ptr : Reuse_W);
    p = start;
    if( c == L'0' ) {
        c = get_non_eof();
        if( IS_X(c) ) {
            radix = 16;
            if( Prep ) {
                *p++ = L'0';
                *p++ = L'x';
            }
            for(c = get_non_eof(); LXC_IS_XDIGIT(c); c = get_non_eof()) {
                /* no check for overflow? */
                *p++ = c;
            }
            if((p == Reuse_W) && (Tiny_lexer_nesting == 0)) {
                strcpy (Msg_Text, GET_MSG (2153));
                error(2153);
            }
            goto check_suffix;
        }
        else {
            radix = 8;
            *p++ = L'0'; /* for preprocessing or 0.xxx case */
        }
    }
    else {
        radix = 10;
    }

    while( LXC_IS_DIGIT((WCHAR)c) ) {
        *p++ = c;
        c = get_non_eof();
    }

    if( IS_DOT(c) || IS_E(c) ) {
        UNGETCH();
        return(get_real(p));
    }

check_suffix:
    if( IS_EL(c) ) {
        if( Prep ) {
            *p++ = c;
        }
        c = get_non_eof();
        if( IS_U(c) ) {
            if(Prep) {
                *p++ = c;
            }
            tok = L_LONGUNSIGNED;
        }
        else {
            tok = L_LONGINT;
            UNGETCH();
        }
    }
    else if( IS_U(c) ) {
        if( Prep ) {
            *p++ = c;
        }
        c = get_non_eof();
        if( IS_EL(c) ) {
            if( Prep ) {
                *p++ = c;
            }
            tok = L_LONGUNSIGNED;
        }
        else {
            tok = L_CUNSIGNED;
            UNGETCH();
        }
    }
    else {
        UNGETCH();
    }
    *p = L'\0';
    if( start == Exp_ptr ) {
        Exp_ptr = p;
        return(L_NOTOKEN);
    }
    else if( Prep ) {
        myfwrite( Reuse_W, (size_t)(p - Reuse_W) * sizeof(WCHAR), 1, OUTPUTFILE);
        return(L_NOTOKEN);
    }
    value.v_long = matol(Reuse_W,radix);
    switch(tok) {
    case L_CINTEGER:
        tok = (radix == 10)
            ? c_size(value.v_long)
            : uc_size(value.v_long)
            ;
        break;
    case L_LONGINT:
        tok = l_size(value.v_long);
        break;
    case L_CUNSIGNED:
        tok = ul_size(value.v_long);
        break;
    }
    yylval.yy_tree = build_const(tok, &value);
    return(tok);
}
/*
 * Read directory structure and store entries in `entries', which must initially
 * point to an empty list.
 */
static int
read_path(struct mtree_reader *r, const char *path, struct mtree_entry **entries,
    struct mtree_entry *parent)
{
	DIR			*dirp;
	struct dirent		*dp;
	struct mtree_entry	*entry;
	struct mtree_entry	*dirs = NULL;
	struct mtree_entry	*dot = NULL;
	char			*wd = NULL;
	int			 ret;
	int			 skip;
	int			 skip_children;

	if (parent == NULL) {
		entry = mtree_entry_create(path);
		if (entry == NULL)
			return (-1);
		ret = read_path_file(r, entry, &skip, &skip_children);
		if (ret == -1) {
			mtree_entry_free(entry);
			return (-1);
		}
		/*
		 * If the path doesn't point to a directory, simply store
		 * the single entry.
		 */
		if (entry->data.type != MTREE_ENTRY_DIR) {
			*entries = entry;
			return (0);
		}
		mtree_entry_free(entry);

		if ((r->options & MTREE_READ_PATH_DONT_CROSS_MOUNT) != 0) {
			struct stat st;

			if ((r->options & MTREE_READ_PATH_FOLLOW_SYMLINKS) != 0)
				ret = stat(path, &st);
			else
				ret = lstat(path, &st);
			if (ret == -1) {
				mtree_reader_set_error(r, errno, "`%s'", path);
				return (-1);
			}
			r->base_dev = st.st_dev;
		}

		/*
		 * Change to the directory to be able to read the files
		 * with the mtree's "./" prefix.
		 */
		wd = mtree_getcwd();
		if (wd == NULL) {
			mtree_reader_set_error(r, errno,
			    "Could not determine the current working directory");
			return (-1);
		}
		if (chdir(path) == -1) {
			mtree_reader_set_error(r, errno,
			    "Could not change the working directory to `%s'",
			    path);
			return (-1);
		}
	}

	/*
	 * Read the directory structure.
	 */
	ret = 0;
	if ((dirp = opendir((parent != NULL) ? path : ".")) == NULL) {
		if ((r->options & MTREE_READ_PATH_SKIP_ON_ERROR) == 0) {
			ret = -1;
			goto end;
		}
		return (0);
	}
	while ((dp = readdir(dirp)) != NULL) {
		if (IS_DOTDOT(dp->d_name))
			continue;
		/*
		 * Dot is read only in the initial directory.
		 */
		if (parent != NULL && IS_DOT(dp->d_name))
			continue;

		entry = mtree_entry_create_empty();
		if (entry == NULL) {
			ret = -1;
			break;
		}
		entry->name = strdup(dp->d_name);
		if (entry->name == NULL) {
			ret = -1;
			break;
		}
		entry->parent = parent;
		entry->path = create_v1_path(entry);
		if (entry->path == NULL) {
			ret = -1;
			break;
		}

		ret = read_path_file(r, entry, &skip, &skip_children);
		if (ret == -1)
			break;
		if (IS_DOT(dp->d_name)) {
			if (skip)
				mtree_entry_free(entry);
			else
				dot = entry;
			if (skip_children) {
				/*
				 * This is a bit artificial, but when the user
				 * asks to skip children below ".", remove
				 * all the entries, except for the dot itself
				 * (unless the dot is skipped as well).
				 */
				mtree_entry_free_all(*entries);
				mtree_entry_free_all(dirs);
				*entries = NULL;
				dirs = NULL;
				break;
			}
			continue;
		}
		if (skip && skip_children) {
			mtree_entry_free(entry);
			continue;
		}
		if (entry->data.type == MTREE_ENTRY_DIR) {
			if (skip)
				entry->flags |= __MTREE_ENTRY_SKIP;
			if (skip_children)
				entry->flags |= __MTREE_ENTRY_SKIP_CHILDREN;
			dirs = mtree_entry_prepend(dirs, entry);
		} else if (!skip)
			*entries = mtree_entry_prepend(*entries, entry);
	}
end:
	if (ret == 0) {
		if (dot != NULL) {
			/* Put the initial dot at the (reversed) start. */
			*entries = mtree_entry_append(*entries, dot);
		}
		closedir(dirp);

		/* Directories are processed after files. */
		entry = dirs;
		while (entry != NULL) {
			struct mtree_entry *next = entry->next;

			if ((entry->flags & __MTREE_ENTRY_SKIP) == 0) {
				dirs = mtree_entry_unlink(dirs, entry);
				*entries = mtree_entry_prepend(*entries, entry);
			}
			if ((entry->flags & __MTREE_ENTRY_SKIP_CHILDREN) == 0)
				ret = read_path(r, entry->path, entries, entry);

			if (ret == -1)
				break;

			entry = next;
		}
	} else {
		/* Fatal error, clean up and make our way back to the caller. */
		mtree_entry_free_all(*entries);
		*entries = NULL;
	}
	mtree_entry_free_all(dirs);

	if (parent == NULL && chdir(wd) == -1)
		WARN("Could not change the working directory back to `%s'", wd);

	return (ret);
}