summaryrefslogtreecommitdiff
path: root/ghc/lib/cbits/getDirectoryContents.lc
blob: 025aae97512db5da3d79eea2973e18bbd87524d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
%
% (c) The GRASP/AQUA Project, Glasgow University, 1995
%
\subsection[getDirectoryContents.lc]{getDirectoryContents Runtime Support}

\begin{code}

#include "rtsdefs.h"
#include "stgio.h"

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif

#ifndef LINK_MAX
#define LINK_MAX 1024
#endif

/* For cleanup of partial answer on error */

static void
freeEntries(char **entries, int count)
{
    int i;

    for (i = 0; i < count; i++)
	free(entries[i]);
    free(entries);
}

/* 
 * Our caller expects a malloc'ed array of malloc'ed string pointers.
 * To ensure consistency when mixing this with other directory
 * operations, we collect the entire list in one atomic operation,
 * rather than reading the directory lazily.
 */

StgAddr
getDirectoryContents(path)
StgByteArray path;
{
    struct stat sb;
    DIR *dir;
    struct dirent *d;
    char **entries;
    int alloc, count;

    /* Check for an actual directory */
    while (stat(path, &sb) != 0) {
	if (errno != EINTR) {
	    cvtErrno();
	    stdErrno();
	    return NULL;
	}
    }
    if (!S_ISDIR(sb.st_mode)) {
	ghc_errtype = ERR_INAPPROPRIATETYPE;
	ghc_errstr = "not a directory";
	return NULL;
    }

    alloc = LINK_MAX;
    if ((entries = (char **) malloc(alloc * sizeof(char *))) == NULL) {
	ghc_errtype = ERR_RESOURCEEXHAUSTED;
	ghc_errstr = "not enough virtual memory";
	return NULL;
    }
    
    while ((dir = opendir(path)) == NULL) {
	if (errno != EINTR) {
	    cvtErrno();
	    stdErrno();
	    free(entries);
	    return NULL;
	}
    }

    count = 0;
    for (;;) {
        errno = 0;  /* unchanged by readdir on EOF */
	while ((d = readdir(dir)) == NULL) {
	    if (errno == 0) {
		entries[count] = NULL;
		(void) closedir(dir);
		return (StgAddr) entries;
	    } else if (errno != EINTR) {
	        cvtErrno();
	        stdErrno();
		freeEntries(entries, count);
		(void) closedir(dir);
		return NULL;
	    }
	    errno = 0;
	}
	if ((entries[count] = malloc(strlen(d->d_name))) == NULL) {
	    ghc_errtype = ERR_RESOURCEEXHAUSTED;
	    ghc_errstr = "not enough virtual memory";
	    freeEntries(entries, count);
	    (void) closedir(dir);
	    return NULL;
	}
	strcpy(entries[count], d->d_name);
	if (++count == alloc) {
	    alloc += LINK_MAX;
	    if ((entries = (char **) realloc(entries, alloc * sizeof(char *))) == NULL) {
		ghc_errtype = ERR_RESOURCEEXHAUSTED;
		ghc_errstr = "not enough virtual memory";
		freeEntries(entries, count);
		(void) closedir(dir);
		return NULL;
	    }
	}
    }
}

\end{code}