1 /*
2 * Directory routines for CUPS.
3 *
4 * This set of APIs abstracts enumeration of directory entries.
5 *
6 * Copyright © 2022-2024 by OpenPrinting.
7 * Copyright © 2007-2021 by Apple Inc.
8 * Copyright © 1997-2005 by Easy Software Products, all rights reserved.
9 *
10 * Licensed under Apache License v2.0. See the file "LICENSE" for more
11 * information.
12 */
13
14 /*
15 * Include necessary headers...
16 */
17
18 #include "string-private.h"
19 #include "debug-internal.h"
20 #include "dir.h"
21
22
23 /*
24 * Windows implementation...
25 */
26
27 #ifdef _WIN32
28 # include <windows.h>
29
30 /*
31 * Types and structures...
32 */
33
34 struct _cups_dir_s /**** Directory data structure ****/
35 {
36 char directory[1024]; /* Directory filename */
37 HANDLE dir; /* Directory handle */
38 cups_dentry_t entry; /* Directory entry */
39 };
40
41
42 /*
43 * '_cups_dir_time()' - Convert a FILETIME value to a UNIX time value.
44 */
45
46 time_t /* O - UNIX time */
_cups_dir_time(FILETIME ft)47 _cups_dir_time(FILETIME ft) /* I - File time */
48 {
49 ULONGLONG val; /* File time in 0.1 usecs */
50
51
52 /*
53 * Convert file time (1/10 microseconds since Jan 1, 1601) to UNIX
54 * time (seconds since Jan 1, 1970). There are 11,644,732,800 seconds
55 * between them...
56 */
57
58 val = ft.dwLowDateTime + ((ULONGLONG)ft.dwHighDateTime << 32);
59 return ((time_t)(val / 10000000 - 11644732800));
60 }
61
62
63 /*
64 * 'cupsDirClose()' - Close a directory.
65 *
66 * @since CUPS 1.2/macOS 10.5@
67 */
68
69 void
cupsDirClose(cups_dir_t * dp)70 cupsDirClose(cups_dir_t *dp) /* I - Directory pointer */
71 {
72 /*
73 * Range check input...
74 */
75
76 if (!dp)
77 return;
78
79 /*
80 * Close an open directory handle...
81 */
82
83 if (dp->dir != INVALID_HANDLE_VALUE)
84 FindClose(dp->dir);
85
86 /*
87 * Free memory used...
88 */
89
90 free(dp);
91 }
92
93
94 /*
95 * 'cupsDirOpen()' - Open a directory.
96 *
97 * @since CUPS 1.2/macOS 10.5@
98 */
99
100 cups_dir_t * /* O - Directory pointer or @code NULL@ if the directory could not be opened. */
cupsDirOpen(const char * directory)101 cupsDirOpen(const char *directory) /* I - Directory name */
102 {
103 cups_dir_t *dp; /* Directory */
104
105
106 /*
107 * Range check input...
108 */
109
110 if (!directory)
111 return (NULL);
112
113 /*
114 * Allocate memory for the directory structure...
115 */
116
117 dp = (cups_dir_t *)calloc(1, sizeof(cups_dir_t));
118 if (!dp)
119 return (NULL);
120
121 /*
122 * Copy the directory name for later use...
123 */
124
125 dp->dir = INVALID_HANDLE_VALUE;
126
127 snprintf(dp->directory, sizeof(dp->directory), "%s\\*", directory);
128
129 /*
130 * Return the new directory structure...
131 */
132
133 return (dp);
134 }
135
136
137 /*
138 * 'cupsDirRead()' - Read the next directory entry.
139 *
140 * @since CUPS 1.2/macOS 10.5@
141 */
142
143 cups_dentry_t * /* O - Directory entry or @code NULL@ if there are no more */
cupsDirRead(cups_dir_t * dp)144 cupsDirRead(cups_dir_t *dp) /* I - Directory pointer */
145 {
146 WIN32_FIND_DATAA entry; /* Directory entry data */
147
148
149 /*
150 * Range check input...
151 */
152
153 if (!dp)
154 return (NULL);
155
156 /*
157 * See if we have already started finding files...
158 */
159
160 if (dp->dir == INVALID_HANDLE_VALUE)
161 {
162 /*
163 * No, find the first file...
164 */
165
166 dp->dir = FindFirstFileA(dp->directory, &entry);
167 if (dp->dir == INVALID_HANDLE_VALUE)
168 return (NULL);
169 }
170 else if (!FindNextFileA(dp->dir, &entry))
171 return (NULL);
172
173 /*
174 * Loop until we have something other than "." or ".."...
175 */
176
177 while (!strcmp(entry.cFileName, ".") || !strcmp(entry.cFileName, ".."))
178 {
179 if (!FindNextFileA(dp->dir, &entry))
180 return (NULL);
181 }
182
183 /*
184 * Copy the name over and convert the file information...
185 */
186
187 strlcpy(dp->entry.filename, entry.cFileName, sizeof(dp->entry.filename));
188
189 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
190 dp->entry.fileinfo.st_mode = 0755 | S_IFDIR;
191 else
192 dp->entry.fileinfo.st_mode = 0644 | S_IFREG;
193
194 dp->entry.fileinfo.st_atime = _cups_dir_time(entry.ftLastAccessTime);
195 dp->entry.fileinfo.st_ctime = _cups_dir_time(entry.ftCreationTime);
196 dp->entry.fileinfo.st_mtime = _cups_dir_time(entry.ftLastWriteTime);
197 dp->entry.fileinfo.st_size = entry.nFileSizeLow + ((unsigned long long)entry.nFileSizeHigh << 32);
198
199 /*
200 * Return the entry...
201 */
202
203 return (&(dp->entry));
204 }
205
206
207 /*
208 * 'cupsDirRewind()' - Rewind to the start of the directory.
209 *
210 * @since CUPS 1.2/macOS 10.5@
211 */
212
213 void
cupsDirRewind(cups_dir_t * dp)214 cupsDirRewind(cups_dir_t *dp) /* I - Directory pointer */
215 {
216 /*
217 * Range check input...
218 */
219
220 if (!dp)
221 return;
222
223 /*
224 * Close an open directory handle...
225 */
226
227 if (dp->dir != INVALID_HANDLE_VALUE)
228 {
229 FindClose(dp->dir);
230 dp->dir = INVALID_HANDLE_VALUE;
231 }
232 }
233
234
235 #else
236
237 /*
238 * POSIX implementation...
239 */
240
241 # include <sys/types.h>
242 # include <dirent.h>
243
244
245 /*
246 * Types and structures...
247 */
248
249 struct _cups_dir_s /**** Directory data structure ****/
250 {
251 char directory[1024]; /* Directory filename */
252 DIR *dir; /* Directory file */
253 cups_dentry_t entry; /* Directory entry */
254 };
255
256
257 /*
258 * 'cupsDirClose()' - Close a directory.
259 *
260 * @since CUPS 1.2/macOS 10.5@
261 */
262
263 void
cupsDirClose(cups_dir_t * dp)264 cupsDirClose(cups_dir_t *dp) /* I - Directory pointer */
265 {
266 DEBUG_printf(("cupsDirClose(dp=%p)", (void *)dp));
267
268 /*
269 * Range check input...
270 */
271
272 if (!dp)
273 return;
274
275 /*
276 * Close the directory and free memory...
277 */
278
279 closedir(dp->dir);
280 free(dp);
281 }
282
283
284 /*
285 * 'cupsDirOpen()' - Open a directory.
286 *
287 * @since CUPS 1.2/macOS 10.5@
288 */
289
290 cups_dir_t * /* O - Directory pointer or @code NULL@ if the directory could not be opened. */
cupsDirOpen(const char * directory)291 cupsDirOpen(const char *directory) /* I - Directory name */
292 {
293 cups_dir_t *dp; /* Directory */
294
295
296 DEBUG_printf(("cupsDirOpen(directory=\"%s\")", directory));
297
298 /*
299 * Range check input...
300 */
301
302 if (!directory)
303 return (NULL);
304
305 /*
306 * Allocate memory for the directory structure...
307 */
308
309 dp = (cups_dir_t *)calloc(1, sizeof(cups_dir_t));
310 if (!dp)
311 return (NULL);
312
313 /*
314 * Open the directory...
315 */
316
317 dp->dir = opendir(directory);
318 if (!dp->dir)
319 {
320 free(dp);
321 return (NULL);
322 }
323
324 /*
325 * Copy the directory name for later use...
326 */
327
328 strlcpy(dp->directory, directory, sizeof(dp->directory));
329
330 /*
331 * Return the new directory structure...
332 */
333
334 return (dp);
335 }
336
337
338 /*
339 * 'cupsDirRead()' - Read the next directory entry.
340 *
341 * @since CUPS 1.2/macOS 10.5@
342 */
343
344 cups_dentry_t * /* O - Directory entry or @code NULL@ when there are no more */
cupsDirRead(cups_dir_t * dp)345 cupsDirRead(cups_dir_t *dp) /* I - Directory pointer */
346 {
347 struct dirent *entry; /* Pointer to entry */
348 char filename[1024]; /* Full filename */
349
350
351 DEBUG_printf(("2cupsDirRead(dp=%p)", (void *)dp));
352
353 /*
354 * Range check input...
355 */
356
357 if (!dp)
358 return (NULL);
359
360 /*
361 * Try reading an entry that is not "." or ".."...
362 */
363
364 for (;;)
365 {
366 /*
367 * Read the next entry...
368 */
369
370 if ((entry = readdir(dp->dir)) == NULL)
371 {
372 DEBUG_puts("3cupsDirRead: readdir() returned a NULL pointer!");
373 return (NULL);
374 }
375
376 DEBUG_printf(("4cupsDirRead: readdir() returned \"%s\"...", entry->d_name));
377
378 /*
379 * Skip "." and ".."...
380 */
381
382 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
383 continue;
384
385 /*
386 * Copy the name over and get the file information...
387 */
388
389 strlcpy(dp->entry.filename, entry->d_name, sizeof(dp->entry.filename));
390
391 snprintf(filename, sizeof(filename), "%s/%s", dp->directory, entry->d_name);
392
393 if (stat(filename, &(dp->entry.fileinfo)))
394 {
395 DEBUG_printf(("3cupsDirRead: stat() failed for \"%s\" - %s...", filename,
396 strerror(errno)));
397 continue;
398 }
399
400 /*
401 * Return the entry...
402 */
403
404 return (&(dp->entry));
405 }
406 }
407
408
409 /*
410 * 'cupsDirRewind()' - Rewind to the start of the directory.
411 *
412 * @since CUPS 1.2/macOS 10.5@
413 */
414
415 void
cupsDirRewind(cups_dir_t * dp)416 cupsDirRewind(cups_dir_t *dp) /* I - Directory pointer */
417 {
418 DEBUG_printf(("cupsDirRewind(dp=%p)", (void *)dp));
419
420 /*
421 * Range check input...
422 */
423
424 if (!dp)
425 return;
426
427 /*
428 * Rewind the directory...
429 */
430
431 rewinddir(dp->dir);
432 }
433 #endif /* _WIN32 */
434