summaryrefslogtreecommitdiffstats
path: root/recipes/python/python-opendir/opendir.pyx
blob: 1ee81f7382d598638ab183847c6e2676bdaf9898 (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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
##############################################################
#
#   opendir.pyx - A class exposing the functionality of
#   ===========   the opendir() family of C libary functions.
#
#   By Gregory Ewing
#   greg.ewing@canterbury.ac.nz
#
#   This software and derivative works created from it
#   may be used and redistributed without restriction.
#
##############################################################

cdef extern from "sys/errno.h":
	int errno

cdef extern from "stdio.h":
	char *strerror(int)

cdef extern from "dirent.h":
	ctypedef struct DIR
	struct dirent:
		int d_namlen
		char d_name[1]
	DIR *c_opendir "opendir" (char *)
	int readdir_r(DIR *, dirent *, dirent **)
	long telldir(DIR *)
	void seekdir(DIR *, long)
	void rewinddir(DIR *)
	int closedir(DIR *)
	int dirfd(DIR *)

#------------------------------------------------------------------

cdef class opendir:
	"""opendir(pathname) --> an open directory object
	
	Opens a directory and provides incremental access to
	the filenames it contains. May be used as a file-like
	object or as an iterator.
	
	When used as a file-like object, each call to read()
	returns one filename, or an empty string when the end
	of the directory is reached. The close() method should
	be called when finished with the directory.
	
	The close() method should also be called when used as
	an iterator and iteration is stopped prematurely. If
	iteration proceeds to completion, the directory is
	closed automatically."""

	cdef DIR *dir
	
	def __cinit__(self, char *path):
		self.dir = c_opendir(path)
		if not self.dir:
			raise IOError(errno, "%s: '%s'" % (strerror(errno), path))
	
	def __dealloc__(self):
		if self.dir:
			closedir(self.dir)

	def read(self):
		"""read() --> filename or empty string
		
		Returns the next filename from the directory, or an empty
		string if the end of the directory has been reached."""
		
		cdef dirent entry, *result
		check_open(self)
		if readdir_r(self.dir, &entry, &result) < 0:
			raise IOError(errno)
		if result:
			return entry.d_name
		else:
			return ""
	
	def tell(self):
		"""tell() --> position
		
		Returns a value representing the current position in the
		directory, suitable for passing to tell(). Only valid for
		this directory object as long as it remains open."""
		
		check_open(self)
		return telldir(self.dir)

	def seek(self, long pos):
		"""seek(position)
		
		Returns the directory to the specified position, which
		should be a value previously returned by tell()."""
		
		check_open(self)
		seekdir(self.dir, pos)
	
	def rewind(self):
		"""rewind()
		
		Resets the position to the beginning of the directory."""
		
		check_open(self)
		rewinddir(self.dir)
	
	def close(self):
		"""close()
		
		Closes the directory and frees the underlying file descriptor."""
		
		if self.dir:
			if closedir(self.dir) < 0:
				raise IOError(errno)
			self.dir = NULL

#  MaxOSX doesn't seem to have dirfd, despite what the
#  man page says. :-(
#
#	def fileno(self):
#		"""fileno() --> file descriptor
#		
#		Returns the file descriptor associated with the open directory."""
#
#		check_open(self)
#		return dirfd(self.dir)

	def __iter__(self):
		return self
	
	def __next__(self):
		"""next() --> filename
		
		Returns the next filename from the directory. If the end of the
		directory has been reached, closes the directory and raises
		StopIteration."""

		if self.dir:
			result = self.read()
			if result:
				return result
			self.close()
		raise StopIteration

#------------------------------------------------------------------

cdef int check_open(opendir d) except -1:
	if not d.dir:
		raise ValueError("Directory is closed")
	return 0