Branch data Line data Source code
1 : : /* GLIB - Library of useful routines for C programming
2 : : * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 : : *
4 : : * gdir.c: Simplified wrapper around the DIRENT functions.
5 : : *
6 : : * Copyright 2001 Hans Breuer
7 : : * Copyright 2004 Tor Lillqvist
8 : : *
9 : : * SPDX-License-Identifier: LGPL-2.1-or-later
10 : : *
11 : : * This library is free software; you can redistribute it and/or
12 : : * modify it under the terms of the GNU Lesser General Public
13 : : * License as published by the Free Software Foundation; either
14 : : * version 2.1 of the License, or (at your option) any later version.
15 : : *
16 : : * This library is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 : : * Lesser General Public License for more details.
20 : : *
21 : : * You should have received a copy of the GNU Lesser General Public
22 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
23 : : */
24 : :
25 : : #include "config.h"
26 : :
27 : : #include <errno.h>
28 : : #include <string.h>
29 : : #include <stdio.h>
30 : : #include <sys/stat.h>
31 : :
32 : : #ifdef HAVE_DIRENT_H
33 : : #include <sys/types.h>
34 : : #include <dirent.h>
35 : : #endif
36 : :
37 : : #include "gdir.h"
38 : :
39 : : #include "gconvert.h"
40 : : #include "gfileutils.h"
41 : : #include "gstrfuncs.h"
42 : : #include "gtestutils.h"
43 : : #include "glibintl.h"
44 : :
45 : : #if defined (_MSC_VER) && !defined (HAVE_DIRENT_H)
46 : : #include "dirent/dirent.h"
47 : : #endif
48 : :
49 : : #include "glib-private.h" /* g_dir_open_with_errno, g_dir_new_from_dirp */
50 : :
51 : : /**
52 : : * GDir:
53 : : *
54 : : * An opaque structure representing an opened directory.
55 : : */
56 : :
57 : : struct _GDir
58 : : {
59 : : gatomicrefcount ref_count;
60 : : #ifdef G_OS_WIN32
61 : : _WDIR *wdirp;
62 : : #else
63 : : DIR *dirp;
64 : : #endif
65 : : #ifdef G_OS_WIN32
66 : : /* maximum encoding of FILENAME_MAX UTF-8 characters, plus a nul terminator
67 : : * (FILENAME_MAX is not guaranteed to include one) */
68 : : gchar utf8_buf[FILENAME_MAX*4 + 1];
69 : : #endif
70 : : };
71 : :
72 : : /*< private >
73 : : * g_dir_open_with_errno:
74 : : * @path: the path to the directory you are interested in.
75 : : * @flags: Currently must be set to 0. Reserved for future use.
76 : : *
77 : : * Opens a directory for reading.
78 : : *
79 : : * This function is equivalent to g_dir_open() except in the error case,
80 : : * errno will be set accordingly.
81 : : *
82 : : * This is useful if you want to construct your own error message.
83 : : *
84 : : * Returns: a newly allocated #GDir on success, or %NULL on failure,
85 : : * with errno set accordingly.
86 : : *
87 : : * Since: 2.38
88 : : */
89 : : GDir *
90 : 552 : g_dir_open_with_errno (const gchar *path,
91 : : guint flags)
92 : : {
93 : : #ifdef G_OS_WIN32
94 : : GDir *dir;
95 : : _WDIR *wdirp;
96 : : gint saved_errno;
97 : : wchar_t *wpath;
98 : : #else
99 : : DIR *dirp;
100 : : #endif
101 : :
102 : 552 : g_return_val_if_fail (path != NULL, NULL);
103 : :
104 : : #ifdef G_OS_WIN32
105 : : wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
106 : :
107 : : g_return_val_if_fail (wpath != NULL, NULL);
108 : :
109 : : wdirp = _wopendir (wpath);
110 : : saved_errno = errno;
111 : : g_free (wpath);
112 : : errno = saved_errno;
113 : :
114 : : if (wdirp == NULL)
115 : : return NULL;
116 : :
117 : : dir = g_new0 (GDir, 1);
118 : : g_atomic_ref_count_init (&dir->ref_count);
119 : : dir->wdirp = wdirp;
120 : :
121 : : return g_steal_pointer (&dir);
122 : : #else
123 : 552 : dirp = opendir (path);
124 : :
125 : 552 : if (dirp == NULL)
126 : 312 : return NULL;
127 : :
128 : 240 : return g_dir_new_from_dirp (dirp);
129 : : #endif
130 : : }
131 : :
132 : : /**
133 : : * g_dir_open: (constructor)
134 : : * @path: the path to the directory you are interested in. On Unix
135 : : * in the on-disk encoding. On Windows in UTF-8
136 : : * @flags: Currently must be set to 0. Reserved for future use.
137 : : * @error: return location for a #GError, or %NULL.
138 : : * If non-%NULL, an error will be set if and only if
139 : : * g_dir_open() fails.
140 : : *
141 : : * Opens a directory for reading. The names of the files in the
142 : : * directory can then be retrieved using g_dir_read_name(). Note
143 : : * that the ordering is not defined.
144 : : *
145 : : * Returns: (transfer full): a newly allocated #GDir on success, %NULL on failure.
146 : : * If non-%NULL, you must free the result with g_dir_close()
147 : : * when you are finished with it.
148 : : **/
149 : : GDir *
150 : 552 : g_dir_open (const gchar *path,
151 : : guint flags,
152 : : GError **error)
153 : : {
154 : : gint saved_errno;
155 : : GDir *dir;
156 : :
157 : 552 : dir = g_dir_open_with_errno (path, flags);
158 : :
159 : 552 : if (dir == NULL && error != NULL)
160 : : {
161 : : gchar *utf8_path;
162 : :
163 : 1 : saved_errno = errno;
164 : :
165 : 1 : utf8_path = g_filename_to_utf8 (path, -1, NULL, NULL, NULL);
166 : :
167 : 1 : g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
168 : : _("Error opening directory ā%sā: %s"), utf8_path, g_strerror (saved_errno));
169 : 1 : g_free (utf8_path);
170 : : }
171 : :
172 : 552 : return dir;
173 : : }
174 : :
175 : : /*< private >
176 : : * g_dir_new_from_dirp:
177 : : * @dirp: a #DIR* created by opendir() or fdopendir()
178 : : *
179 : : * Creates a #GDir object from the DIR object that is created using
180 : : * opendir() or fdopendir(). The created #GDir assumes ownership of the
181 : : * passed-in #DIR pointer.
182 : : *
183 : : * @dirp must not be %NULL.
184 : : *
185 : : * This function never fails.
186 : : *
187 : : * Returns: a newly allocated #GDir, which should be closed using
188 : : * g_dir_close().
189 : : *
190 : : * Since: 2.38
191 : : **/
192 : : GDir *
193 : 252 : g_dir_new_from_dirp (gpointer dirp)
194 : : {
195 : : #ifdef G_OS_UNIX
196 : : GDir *dir;
197 : :
198 : 252 : g_return_val_if_fail (dirp != NULL, NULL);
199 : :
200 : 252 : dir = g_new0 (GDir, 1);
201 : 252 : g_atomic_ref_count_init (&dir->ref_count);
202 : 252 : dir->dirp = dirp;
203 : :
204 : 252 : return dir;
205 : : #else
206 : : g_assert_not_reached ();
207 : :
208 : : return NULL;
209 : : #endif
210 : : }
211 : :
212 : : /**
213 : : * g_dir_read_name:
214 : : * @dir: a #GDir* created by g_dir_open()
215 : : *
216 : : * Retrieves the name of another entry in the directory, or %NULL.
217 : : * The order of entries returned from this function is not defined,
218 : : * and may vary by file system or other operating-system dependent
219 : : * factors.
220 : : *
221 : : * %NULL may also be returned in case of errors. On Unix, you can
222 : : * check `errno` to find out if %NULL was returned because of an error.
223 : : *
224 : : * On Unix, the '.' and '..' entries are omitted, and the returned
225 : : * name is in the on-disk encoding.
226 : : *
227 : : * On Windows, as is true of all GLib functions which operate on
228 : : * filenames, the returned name is in UTF-8.
229 : : *
230 : : * Returns: (type filename): The entry's name or %NULL if there are no
231 : : * more entries. The return value is owned by GLib and
232 : : * must not be modified or freed.
233 : : **/
234 : : const gchar *
235 : 2072 : g_dir_read_name (GDir *dir)
236 : : {
237 : : #ifdef G_OS_WIN32
238 : : gchar *utf8_name;
239 : : struct _wdirent *wentry;
240 : : #else
241 : : struct dirent *entry;
242 : : #endif
243 : :
244 : 2072 : g_return_val_if_fail (dir != NULL, NULL);
245 : :
246 : : #ifdef G_OS_WIN32
247 : : while (1)
248 : : {
249 : : wentry = _wreaddir (dir->wdirp);
250 : : while (wentry
251 : : && (0 == wcscmp (wentry->d_name, L".") ||
252 : : 0 == wcscmp (wentry->d_name, L"..")))
253 : : wentry = _wreaddir (dir->wdirp);
254 : :
255 : : if (wentry == NULL)
256 : : return NULL;
257 : :
258 : : utf8_name = g_utf16_to_utf8 (wentry->d_name, -1, NULL, NULL, NULL);
259 : :
260 : : if (utf8_name == NULL)
261 : : continue; /* Huh, impossible? Skip it anyway */
262 : :
263 : : strcpy (dir->utf8_buf, utf8_name);
264 : : g_free (utf8_name);
265 : :
266 : : return dir->utf8_buf;
267 : : }
268 : : #else
269 : 2072 : entry = readdir (dir->dirp);
270 : 2072 : while (entry
271 : 2572 : && (0 == strcmp (entry->d_name, ".") ||
272 : 2073 : 0 == strcmp (entry->d_name, "..")))
273 : 500 : entry = readdir (dir->dirp);
274 : :
275 : 2072 : if (entry)
276 : 1823 : return entry->d_name;
277 : : else
278 : 249 : return NULL;
279 : : #endif
280 : : }
281 : :
282 : : /**
283 : : * g_dir_rewind:
284 : : * @dir: a #GDir* created by g_dir_open()
285 : : *
286 : : * Resets the given directory. The next call to g_dir_read_name()
287 : : * will return the first entry again.
288 : : **/
289 : : void
290 : 1 : g_dir_rewind (GDir *dir)
291 : : {
292 : 1 : g_return_if_fail (dir != NULL);
293 : :
294 : : #ifdef G_OS_WIN32
295 : : _wrewinddir (dir->wdirp);
296 : : #else
297 : 1 : rewinddir (dir->dirp);
298 : : #endif
299 : : }
300 : :
301 : : static void
302 : 503 : g_dir_actually_close (GDir *dir)
303 : : {
304 : : #ifdef G_OS_WIN32
305 : : g_clear_pointer (&dir->wdirp, _wclosedir);
306 : : #else
307 : 503 : g_clear_pointer (&dir->dirp, closedir);
308 : : #endif
309 : 503 : }
310 : :
311 : : /**
312 : : * g_dir_close:
313 : : * @dir: (transfer full): a #GDir* created by g_dir_open()
314 : : *
315 : : * Closes the directory immediately and decrements the reference count.
316 : : *
317 : : * Once the reference count reaches zero, the `GDir` structure itself will be
318 : : * freed. Prior to GLib 2.80, `GDir` was not reference counted.
319 : : *
320 : : * It is an error to call any of the `GDir` methods other than
321 : : * [method@GLib.Dir.ref] and [method@GLib.Dir.unref] on a `GDir` after calling
322 : : * [method@GLib.Dir.close] on it.
323 : : **/
324 : : void
325 : 251 : g_dir_close (GDir *dir)
326 : : {
327 : 251 : g_return_if_fail (dir != NULL);
328 : :
329 : 251 : g_dir_actually_close (dir);
330 : 251 : g_dir_unref (dir);
331 : : }
332 : :
333 : : /**
334 : : * g_dir_ref:
335 : : * @dir: (transfer none): a `GDir`
336 : : *
337 : : * Increment the reference count of `dir`.
338 : : *
339 : : * Returns: (transfer full): the same pointer as `dir`
340 : : * Since: 2.80
341 : : */
342 : : GDir *
343 : 1 : g_dir_ref (GDir *dir)
344 : : {
345 : 1 : g_return_val_if_fail (dir != NULL, NULL);
346 : :
347 : 1 : g_atomic_ref_count_inc (&dir->ref_count);
348 : 1 : return dir;
349 : : }
350 : :
351 : : /**
352 : : * g_dir_unref:
353 : : * @dir: (transfer full): a `GDir`
354 : : *
355 : : * Decrements the reference count of `dir`.
356 : : *
357 : : * Once the reference count reaches zero, the directory will be closed and all
358 : : * resources associated with it will be freed. If [method@GLib.Dir.close] is
359 : : * called when the reference count is greater than zero, the directory is closed
360 : : * but the `GDir` structure will not be freed until its reference count reaches
361 : : * zero.
362 : : *
363 : : * It is an error to call any of the `GDir` methods other than
364 : : * [method@GLib.Dir.ref] and [method@GLib.Dir.unref] on a `GDir` after calling
365 : : * [method@GLib.Dir.close] on it.
366 : : *
367 : : * Since: 2.80
368 : : */
369 : : void
370 : 253 : g_dir_unref (GDir *dir)
371 : : {
372 : 253 : g_return_if_fail (dir != NULL);
373 : :
374 : 253 : if (g_atomic_ref_count_dec (&dir->ref_count))
375 : : {
376 : 252 : g_dir_actually_close (dir);
377 : 252 : g_free (dir);
378 : : }
379 : : }
380 : :
381 : : #ifdef G_OS_WIN32
382 : :
383 : : /* Binary compatibility versions. Not for newly compiled code. */
384 : :
385 : : _GLIB_EXTERN GDir *g_dir_open_utf8 (const gchar *path,
386 : : guint flags,
387 : : GError **error);
388 : : _GLIB_EXTERN const gchar *g_dir_read_name_utf8 (GDir *dir);
389 : :
390 : : GDir *
391 : : g_dir_open_utf8 (const gchar *path,
392 : : guint flags,
393 : : GError **error)
394 : : {
395 : : return g_dir_open (path, flags, error);
396 : : }
397 : :
398 : : const gchar *
399 : : g_dir_read_name_utf8 (GDir *dir)
400 : : {
401 : : return g_dir_read_name (dir);
402 : : }
403 : :
404 : : #endif
|